12 Other Considerations

This section covers other situations that affect garbage collection.

Finalization and Weak, Soft, and Phantom References

Some applications interact with garbage collection by using finalization and weak, soft, or phantom references.

However, the use of finalization is discouraged. It can lead to problems with security, performance, and reliability. For instance, relying on finalization to close file descriptors makes an external resource (descriptors) dependent on garbage collection promptness.

Note:

Finalization has been deprecated in JDK 9.

Finalization

A class can declare a finalizer – the method protected void finalize() – whose body releases any underlying resources. The GC will schedule the finalizer of an unreachable object, which is called before the GC reclaims the object's memory.

An object becomes unreachable, and thus eligible for garbage collection, when there’s no path from a GC root to the object. GC roots include references from an active thread and internal JVM references; they are the references that keep objects in memory.

See Monitoring the Objects Pending Finalization in Java Platform, Standard Edition Troubleshooting Guide to determine if finalizable objects are building up in your system. In addition, you can use one of these tools:

  • JDK Mission Control:
    1. In the JVM Browser, right-click your JVM and select Start JMX Console.
    2. In the MBean Browser, in the MBean Tree, expand java.lang and select Memory.
    3. In MBean Features, the attribute ObjectPendingFinalizationCount is the approximate number of objects that are pending finalization.
  • jcmd tool:
    • Run the following command to print information about the Java finalization queue; the value <pid> is the PID of your JVM:

      jcmd <pid> GC.finalizer_info

Migrating from Finalization

To avoid finalization, use one of the following techniques:

The try-with-Resources Statement

The try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the code block, even if one or more exceptions occur. See The Try-with-resources Statement for more information.

The Cleaner API

If you foresee that the lifecycle of a resource in your application will live beyond the scope of a try-with-resources statement, then you can use the Cleaner API instead. The Cleaner API allows a program to register a cleaning action for an object that is run some time after the object becomes unreachable.

Cleaners enable you to avoid many of the drawbacks of finalizers:

  • More secure: A cleaner must explicitly register an object. In addition, cleaning actions cannot access it so object resurrection is impossible.
  • Better performance: You have more control over when you register a cleaning action, which means a cleaning action never processes an uninitialized or partially initialized object. You can also cancel an object's cleaning action.
  • More reliable: You can control which threads run cleaning actions.

However, like finalizers, the garbage collector schedules cleaning actions, so they may suffer from unbounded delays. Thus, don’t use the cleaner API in situations where the timely release of a resource is required.

The following is a simple example of a cleaner. It does the following:

  1. Defines a cleaning action class, State, which initializes the cleaning action and defines the cleaning action itself (by overriding the State::run() method).
  2. Creates an instance of Cleaner.
  3. With this instance of Cleaner, registers the object myObject1 and a cleaning action (an instance of State).
  4. To ensure that the garbage collector schedules the cleaner and the cleaning action State::run() is performed before the example ends, the example:
    1. Sets myObject1 to null to ensure it is phantom unreachable. See .
    2. Calls System.gc() in a loop to trigger garbage collection cleanup.

Figure 12-1 CleanerExample

import java.lang.ref.Cleaner;

public class CleanerExample {
    
    // This Cleaner is shared by all CleanerExample instances
    private static final Cleaner CLEANER = Cleaner.create();
    private final State state;

    public CleanerExample(String id) {
        state = new State(id);
        CLEANER.register(this, state);
    }

    // Cleaning action class for CleanerExample
    private static class State implements Runnable {
        final private String id;

        private State(String id) {
            this.id = id;
            System.out.println("Created cleaning action for " + this.id);
        }

        @Override
        public void run() {
            System.out.println("Cleaner garbage collected " + this.id);
        }
    }

    public static void main(String[] args) {
        CleanerExample myObject1 = new CleanerExample("myObject1");

        // Make myObject1 unreachable
        myObject1 = null;

        System.out.println("-- Give the GC a chance to schedule the Cleaner --");
        for (int i = 0; i < 100; i++) {
            
            // Calling System.gc() in a loop is usually sufficient to trigger
            // cleanup in a small program like this.
            System.gc();
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {}
        }
        System.out.println("-- Finished --");
    }
}

This example prints the following:

Created cleaning action for myObject1
-- Give the GC a chance to schedule the Cleaner --
Cleaner garbage collected myObject1
-- Finished --

Consider the following if you're implementing a cleaner for a production environment:

  • The cleaning action class (State in this example) should be a private implementation detail. In particular, it shouldn't be used from the main(String[]) method. Thus, your cleaning action class should be immutable whenever practical. A new object should handle creating its own cleaning action class and registering itself with a cleaner within its constructor.
  • Classes typically need access to objects within the cleaner action class. The simplest way to do this is for the object to save a reference to the cleaner action class.
  • Cleaner instances should be shared. In this example, all instances of CleanerExample should share a single, static Cleaner instance.

See the JavaDoc API documentation for the Cleaner class for more information about implementing a cleaner.

Reference-Object Types

There are three reference-object types: SoftReference, WeakReference, and PhantomReference. Each reference-object type corresponds to a different level of reachability. The following are the different levels of reachability, from strongest to weakest, which reflect the life cycle of an object:

  • An object is strongly reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strongly reachable by the thread that created it.
  • An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.
  • An object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference. When the weak references to a weakly-reachable object are cleared, the object becomes eligible for finalization.
  • An object is phantom reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.
  • An object is unreachable, and therefore eligible for reclamation, when it is not reachable in any of the previous ways.

Each reference-object type encapsulates a single reference to a particular object, which is called the referent. A reference object provides methods for clearing the referent.

The following are the most common uses for reference-object instances:

  • To maintain access to an object while still allowing it to be garbage collected if the system needs to free up memory (such as a cached value that can be regenerated if required)
  • To determine and perhaps take some action when an object has reached a particular reachability level (in combination with the ReferenceQueue class)

Explicit Garbage Collection

Another way that applications can interact with garbage collection is by calling full garbage collections explicitly by using System.gc().

This can force a major collection to be done when it may not be necessary (for example, when a minor collection would suffice), and so in general should be avoided. The performance effect of explicit garbage collections can be measured by disabling them using the flag -XX:+DisableExplicitGC, which causes the VM to ignore calls to System.gc().

One of the most commonly encountered uses of explicit garbage collection occurs with the distributed garbage collection (DGC) of Remote Method Invocation (RMI). Applications using RMI refer to objects in other virtual machines. Garbage cannot be collected in these distributed applications without occasionally invoking garbage collection of the local heap, so RMI forces full collections periodically. The frequency of these collections can be controlled with properties, as in the following example:

java -Dsun.rmi.dgc.client.gcInterval=3600000
    -Dsun.rmi.dgc.server.gcInterval=3600000 ...

This example specifies explicit garbage collection once per hour instead of the default rate of once per minute. However, this may also cause some objects to take much longer to be reclaimed. These properties can be set as high as Long.MAX_VALUE to make the time between explicit collections effectively infinite if there's no desire for an upper bound on the timeliness of DGC activity.

Soft References

Soft references are kept alive longer in the server virtual machine than in the client.

The rate of clearing can be controlled with the command-line option -XX:SoftRefLRUPolicyMSPerMB=<N>, which specifies the number of milliseconds (ms) a soft reference will be kept alive (once it is no longer strongly reachable) for each megabyte of free space in the heap. The default value is 1000 ms per megabyte, which means that a soft reference will survive (after the last strong reference to the object has been collected) for 1 second for each megabyte of free space in the heap. This is an approximate figure because soft references are cleared only during garbage collection, which may occur sporadically.

Class Metadata

Java classes have an internal representation within Java Hotspot VM and are referred to as class metadata.

In previous releases of Java Hotspot VM, the class metadata was allocated in the so-called permanent generation. Starting with JDK 8, the permanent generation was removed and the class metadata is allocated in native memory. The amount of native memory that can be used for class metadata is by default unlimited. Use the option -XX:MaxMetaspaceSize to put an upper limit on the amount of native memory used for class metadata.

Java Hotspot VM explicitly manages the space used for metadata. Space is requested from the OS and then divided into chunks. A class loader allocates space for metadata from its chunks (a chunk is bound to a specific class loader). When classes are unloaded for a class loader, its chunks are recycled for reuse or returned to the OS. Metadata uses space allocated by mmap, not by malloc.

If -XX:UseCompressedOops is turned on and -XX:UseCompressedClassesPointers is used, then two logically different areas of native memory are used for class metadata. -XX:UseCompressedClassPointers uses a 32-bit offset to represent the class pointer in a 64-bit process as does -XX:UseCompressedOops for Java object references. A region is allocated for these compressed class pointers (the 32-bit offsets). The size of the region can be set with -XX:CompressedClassSpaceSize and is 1 gigabyte (GB) by default. The space for the compressed class pointers is reserved as space allocated by -XX:mmap at initialization and committed as needed. The -XX:MaxMetaspaceSize applies to the sum of the committed compressed class space and the space for the other class metadata.

Class metadata is deallocated when the corresponding Java class is unloaded. Java classes are unloaded as a result of garbage collection, and garbage collections may be induced to unload classes and deallocate class metadata. When the space committed for class metadata reaches a certain level (a high-water mark), a garbage collection is induced. After the garbage collection, the high-water mark may be raised or lowered depending on the amount of space freed from class metadata. The high-water mark would be raised so as not to induce another garbage collection too soon. The high-water mark is initially set to the value of the command-line option -XX:MetaspaceSize. It is raised or lowered based on the options -XX:MaxMetaspaceFreeRatio and -XX:MinMetaspaceFreeRatio. If the committed space available for class metadata as a percentage of the total committed space for class metadata is greater than -XX:MaxMetaspaceFreeRatio, then the high-water mark will be lowered. If it's less than -XX:MinMetaspaceFreeRatio, then the high-water mark will be raised.

Specify a higher value for the option -XX:MetaspaceSize to avoid early garbage collections induced for class metadata. The amount of class metadata allocated for an application is application-dependent and general guidelines do not exist for the selection of -XX:MetaspaceSize. The default size of -XX:MetaspaceSize is platform-dependent and ranges from 12 MB to about 20 MB.

Information about the space used for metadata is included in a printout of the heap. The following is typical output:.

[0,296s][info][gc,heap,exit] Heap
[0,296s][info][gc,heap,exit] garbage-first heap total 514048K, used 0K [0x00000005ca600000, 0x00000005ca8007d8, 0x00000007c0000000)
[0,296s][info][gc,heap,exit] region size 2048K, 1 young (2048K), 0 survivors (0K)
[0,296s][info][gc,heap,exit] Metaspace used 2575K, capacity 4480K, committed 4480K, reserved 1056768K
[0,296s][info][gc,heap,exit] class space used 238K, capacity 384K, committed 384K, reserved 1048576K

In the line beginning with Metaspace, the used value is the amount of space used for loaded classes. The capacity value is the space available for metadata in currently allocated chunks. The committed value is the amount of space available for chunks. The reserved value is the amount of space reserved (but not necessarily committed) for metadata. The line beginning with class space contains the corresponding values for the metadata for compressed class pointers.