The only thing I know about PhantomReference
is,
get()
method, it will always return null
I used a PhantomReference in a unit test to verify that the code under test didn't keep unnessecary references to some object. (Original code)
import static com.google.common.base.Preconditions.checkNotNull;
import static org.fest.assertions.Assertions.assertThat;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import com.google.common.testing.GcFinalization;
/**
* Helps to test for memory leaks
*/
public final class MemoryTester
{
private MemoryTester()
{
}
/**
* A simple {@link PhantomReference} that can be used to assert that all references to it is
* gone.
*/
public static final class FinalizationAwareObject extends PhantomReference<Object>
{
private final WeakReference<Object> weakReference;
private FinalizationAwareObject(Object referent, ReferenceQueue<Object> referenceQueue)
{
super(checkNotNull(referent), referenceQueue);
weakReference = new WeakReference<Object>(referent, referenceQueue);
}
/**
* Runs a full {@link System#gc() GC} and asserts that the reference has been released
* afterwards
*/
public void assertThatNoMoreReferencesToReferentIsKept()
{
String leakedObjectDescription = String.valueOf(weakReference.get());
GcFinalization.awaitFullGc();
assertThat(isEnqueued()).as("Object: " + leakedObjectDescription + " was leaked").isTrue();
}
}
/**
* Creates a {@link FinalizationAwareObject} that will know if {@code referenceToKeepTrackOff}
* has been garbage collected. Call
* {@link FinalizationAwareObject#assertThatNoMoreReferencesToReferentIsKept()} when you expect
* all references to {@code referenceToKeepTrackOff} be gone.
*/
public static FinalizationAwareObject createFinalizationAwareObject(Object referenceToKeepTrackOff)
{
return new FinalizationAwareObject(referenceToKeepTrackOff, new ReferenceQueue<Object>());
}
}
And the test:
@Test
public void testThatHoldingOnToAnObjectIsTreatedAsALeak() throws Exception
{
Object holdMeTight = new String("Hold-me-tight");
FinalizationAwareObject finalizationAwareObject = MemoryTester.createFinalizationAwareObject(holdMeTight);
try
{
finalizationAwareObject.assertThatNoMoreReferencesToReferentIsKept();
fail("holdMeTight was held but memory leak tester did not discover it");
}
catch(AssertionError expected)
{
assertThat(expected).hasMessage("[Object: Hold-me-tight was leaked] expected:<[tru]e> but was:<[fals]e>");
}
}
I found a practical and useful use case of PhantomReference
which is org.apache.commons.io.FileCleaningTracker
in commons-io project. FileCleaningTracker
will delete the physical file when its marker object is garbage collected.
Something to take note is the Tracker
class which extends PhantomReference
class.
A general diced-up table explanation, from the Java Glossary.
Which of course coincides with the PhantomReference documentation:
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.
And last but not least, all the gory details (this is a good read): Java Reference Objects (or How I Learned to Stop Worrying and Love OutOfMemoryError).
Happy coding. (But to answer the question, I've only ever used WeakReferences.)
Great explanation of Phantom Reference usage:
Phantom references are safe way to know an object has been removed from memory. For instance, consider an application that deals with large images. Suppose that we want to load a big image in to memory when large image is already in memory which is ready for garbage collected. In such case, we want to wait until the old image is collected before loading a new one. Here, the phantom reference is flexible and safely option to choose. The reference of the old image will be enqueued in the ReferenceQueue once the old image object is finalized. After receiving that reference, we can load the new image in to memory.
if you use its get() method it will always return null, and not the object. [ then whats the use of it ]
The useful methods to call (rather than get()
) would be isEnqueued()
or referenceQueue.remove()
. You would call these methods to perform some action that needs to take place on the final round of garbage collection of the object.
The first time around is when the object has its finalize()
method called, so you could put closing hooks there too. However, as others have stated, there are probably more sure ways of performing clean up or whatever action needs to take place pre and post garbage collection or, more generally, upon end-of-life of the object.
I found another practical use of PhantomReferences
in LeakDetector class of Jetty.
Jetty uses LeakDetector
class to detect if the client code acquires a resource but never releases it and the LeakDetector
class uses the PhantomReferences
for this purpose.