In Java, memory management is handled automatically, the programmer doesn’t need to worry about reference objects that have been released. There is one downside to this approach, that the programmer cannot know when a particular object will be collected. However, the java.lang.ref package defines classes that provide a limited degree of interaction with the garbage collector. The concrete classes SoftReference, WeakReference and PhantomReference are subclasses of Reference that interact with the garbage collector in different ways. In our previous journal entry we introduced the Reference Queues, but here we will dig a little deep in discussing the functionality and behavior of Phantom Reference classes. We will also see how it can be used.
Before we start understanding the Phantom Reference classes, let’s first understand the problems with the
Finalization. Finalization can be used to perform a cleanup process on objects that garbage collector considers as unreachable. This feature can be utilized to reclaim native resources associated with an object. However, finalizers have many problems associated.
- We can’t foresee the call of finalize(). This is because of the unpredictability nature of Garbage Collection, therefore the calling of finalize() cannot be predicted.
- Finalization can slow down an application execution. Managing objects with a finalize() method take more resources from the JVM than normal objects.
There is no guarantee that the object will be garbage collected. The object might never become eligible for GC because it could be reachable through the entire lifetime of the JVM. It is also possible that no garbage collection actually runs from the time the object became eligible and before JVM stops.
You should also use finalization only when it is absolutely necessary. Finalization is a nondeterministic — and sometimes unpredictable — process. The less you rely on it, the smaller the impact it will have on the JVM and your application
phantom reachable, phantomly reachable
An object is phantom reachable if it is neither strongly, softly, or weakly reachable and has been finalized and there is a path from the roots to it that contains at least one phantom reference.
The PhantomReference constructor accepts two arguments:
referent – the object the new phantom reference will refer to
q – the reference is registered with the given queue.
The argument q represents the instance of the ReferenceQueue class. If the garbage collector determines that the referent of a phantom reference is phantom reachable, then the PhantomReference will be added to this ReferenceQueue. You can then retrieve the PhantomReference by using the remove() methods of the ReferenceQueue class.
Consider the following example,
ReferenceQueue q = new ReferenceQueue(); PhantomReference pr = new PhantomReference(object, referenceQueue); // Later on another point Reference r = q.remove(); // Now, clear up any thing you want
When to use PhantomReference?
Phantom Reference can be used in situations, where sometimes using finalize() is not the sensible thing to do. This reference type differs from the other types defined in java.lang.ref Package because it isn’t meant to be used to access the object, but as a signal that the object has already been finalized, and the garbage collector is ready to reclaim its memory.
Phantom reference objects, which are enqueued after the collector determines that their referents may otherwise be reclaimed. Phantom references are most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism.
Developers tend to use finalize() method to perform the cleanup process of objects which is usually not advisable. As mentioned above, Finalizers have an impact on the performance of the garbage collector since objects with finalizers are slow to garbage collect.
The safest way to know whether an object has been removed from the memory is Phantom References. Let us understand this by taking an example of an application that deals with large data files. We want to load a big file into the memory when there already is another big file in memory which is ready for garbage collection. In such a scenario, we want to wait until the old file is collected before loading a new one. In the scenario like these, phantom references are flexible and safe options to choose. The reference to the old file will be enqueued in the ReferenceQueue object once the old file is finalized. After receiving that reference, we can load the new file into the memory.
In a similar fashion, we can use Phantom References to implement a Connection Pool. We can easily gain control over the number of open connections and can block until one becomes available.
Reference Objects and Garbage Collection
Once there are no strong references to the referent all the Soft References can be garbage collected. All softly reachable objects will be reclaimed before an OutOfMemoryException is thrown.
Weak Reference can be garbage collected when there are no strong or soft references to the referent. However, unlike Soft Reference, they are garbage collected on a gc even when memory is abundant. They often can be used for “canonical mappings” where each object has a unique identifier (one-to-one), and in collections of “listeners”.
On the other hand, Phantom Reference, can be garbage collected once there are no strong, soft or weak references to the referent. When an object is phantomly reachable, it means the object is already finalized but not yet reclaimed, so the GC enqueues it in a ReferenceQueue for post-finalization processing.
Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.
A PhantomReference is not automatically cleared when it is enqueued, so when we remove a PhantomReference from a ReferenceQueue, we must call its clear() method or allow the PhantomReference object itself to be garbage-collected.
We should try to avoid finalize() as much as possible. There is no guarantee if the finalize() method will be called promptly following garbage collection, or even it will be called. If the finalize method runs for a long time, it can delay execution of finalize() methods of other objects. Instead of relying on finalize(), we can use reference types defined in java.lang.ref package.