Have you ever used PhantomReference in any project?
Asked Answered
A

11

96

The only thing I know about PhantomReference is,

  • If you use its get() method, it will always return null and not the object. What's the use of it?
  • By using PhantomReference, you make it sure that the object cannot be resurrected from finalize method.

But what is the use of this concept/class?

Have you ever used this in any of your project or do you have any example where we should use this?

Aleta answered 21/10, 2009 at 6:36 Comment(3)
Also https://mcmap.net/q/77058/-when-to-use-phantom-references-in-java-duplicateBracketing
As you can't get the referred obj of a PhantomReference, its a complete misnomer: It should have been called FakeReference or NonReference.Bracketing
Here's anothre thread with code: https://mcmap.net/q/77059/-how-to-use-phantomreference-as-finalize-replacement/632951Bracketing
G
52

I used PhantomReferences in a simplistic, very specialized kind of memory profiler to monitor object creation and destruction. I needed them to keep track of destruction. But the approach is out-dated. (It was written in 2004 targeting J2SE 1.4.) Professional profiling tools are much more powerful and reliable and the newer Java 5 features like JMX or agents and JVMTI can be used for that too.

PhantomReferences (always used together with the Reference queue) are superior to finalize which has some problems and should therefore be avoided. Mainly making objects reachable again. This could be avoided with the finalizer guardian idiom (-> read more in 'Effective Java'). So they are also the new finalize.

Furthermore, PhantomReferences

allow you to determine exactly when an object was removed from memory. They are in fact the only way to determine that. This isn't generally that useful, but might come in handy in certain very specific circumstances like manipulating large images: if you know for sure that an image should be garbage collected, you can wait until it actually is before attempting to load the next image, and therefore make the dreaded OutOfMemoryError less likely. (Quoted from enicholas.)

And as psd wrote first, Roedy Green has a good summary of references.

Geraldine answered 21/10, 2009 at 6:56 Comment(0)
Y
23

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.)

Yount answered 21/10, 2009 at 6:51 Comment(2)
Btw, a question about that article. In the section on PhantomReference, he keeps a strong reference to connection objects through those two tables. It means that the connections will never become unreachable (assuming the pool instance itself never becomes unreachable). So the corresponding PhantomReferences will never be enqueued, right? Or am I missing something?Miley
Wow, that article from kdgregory deserves a +10Bracketing
T
16

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.

Tyrelltyrian answered 23/7, 2013 at 12:43 Comment(0)
D
13

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.

Doublebank answered 2/7, 2012 at 18:1 Comment(0)
N
7

THIS SHOULD BE OBSOLETE WITH JAVA 9!
Use java.util.Cleaner instead! (Or sun.misc.Cleaner on older JRE)

Original post:


I found that the use of PhantomReferences has nearly the same amount of pitfalls as finalizer methods (but a fewer problems once you get it right). I have written a small solution (a very small framework to use PhantomReferences) for Java 8. It allows to use lambda expressions as callbacks to be run after the object has been removed. You can register the callbacks for inner resources that should be closed. With this I have found a solution that works for me as it makes it much more practical.

https://github.com/claudemartin/java-cleanup

Here's a small example to show how a callback is registered:

  class Foo implements Cleanup {
    //...  
    public Foo() { 
    //...    
      this.registerCleanup((value) -> {
        try {
          // 'value' is 'this.resource'
          value.close();
        } catch (Exception e) {
          logger.warning("closing resource failed", e);
        }
      }, this.resource);
    }

And then there is the even simpler method for auto-close, doing about the same as the above:

this.registerAutoClose(this.resource);

To answer your questions:

[ then whats the use of it ]

You can't clean up something that doesn't exist. But it could have had resources that still exist and need to be cleaned up so they can be removed.

But what is the use of this concept/class?

It's not necessarily to do anything with any effect other than debugging/logging. Or maybe for statistics. I see it more like a notification service from the GC. You could also want to use it to remove aggregated data that becomes irrelevant once the object is removed (but there are probably better solutions for that). Examples often mention database connections to be closed, but I don't see how this is such a good idea as you couldn't work with transactions. An application framework will provide a much better solution for that.

Have you ever used this in any of your project, or do you have any example where we should use this? Or is this concept made just for interview point of view ;)

I use it mostly just for logging. So I can trace the removed elements and see how GC works and can be tweaked. I wouldn't run any critical code in this way. If something needs to be closed then it should be done in a try-with-resource-statement. And I use it in unit tests, to make sure I don't have any memory leaks. The same way as jontejj does it. But my solution is a bit more general.

Nd answered 29/5, 2014 at 9:30 Comment(1)
Yes, just like my solution "java-cleanup". Both are abstractions, so you don't need to deal with them directly.Nd
C
3

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>");
    }
}
Collette answered 5/5, 2013 at 22:46 Comment(0)
O
2

It is common to use WeakReference where PhantomReference is more appropriate. This avoids certain problems of being able to resurrect objects after a WeakReference is cleared/enqueued by the garbage collector. Usually the difference doesn't matter because people are not playing silly buggers.

Using PhantomReference tends to be a bit more intrusive because you can't pretend that the get method works. You can't, for example, write a Phantom[Identity]HashMap.

Oak answered 21/10, 2009 at 10:6 Comment(3)
IdentityHashMap<PhantomReference> is actually one of the appropriate places for an IdentityHashMap. Note that strong reference kept to the PhantomReference, but not the referent.Yount
Do you actually mean that for weak references, finalize may recreate the obj? weakref.get could return null, and then later, it is still able to return the obj?Bracketing
@Bracketing finalize doesn't recreate the object as such. It can make the object strongly reachable again after a WeakReference returns null from get and is enqueued. / (user166390: As in a map keyed on the target of the reference, as WeakHashMap does, not an identity map of references which is fine.)Oak
V
2

I used it in the early days of Android. Back them a BitmapDrawable had an underlying Bitmap that used memory that was not allocated in the Java Heap space, which meant that you were using memory, but the JVM didn't feel the memory pressure. The symptom was the app would crash by running out of memory, but you'd never find that your finalizer was called (or that there hadn't been a garbage collection sweep at all).

So I created a subclass of PhantomReference that had a hard reference to the Bitmap (class variable). The PhantomReference itself pointed to the BitmapDrawable.

When the phantom reference would come off the queue indicating that the BitmapDrawable was no longer reachable, I'd call the release method on the Bitmap which released the memory that was outside of the vm heap space.

It worked great, it more or less completely removed the out of memory failures that occurred because of that weird memory model on early Android.

Android later changed the memory model to load bitmaps into the JVM heap, btw, because you actually want your vm to feel memory pressure if the app is running out of memory. It was definitely a "Black Diamond" situation to have to release the memory like this and I doubt most app developers realized that Garbage Collection wasn't going to help them because the memory was outside the view of the Garbage Collector. They probably just viewed it as an Android bug.

Vaniavanilla answered 17/10, 2021 at 6:5 Comment(0)
S
1

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.

Scholium answered 25/8, 2016 at 12:0 Comment(0)
T
1

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.

Tupelo answered 2/2, 2020 at 15:53 Comment(0)
C
0

Here is a generic example of using it: In this case, for some Swing code in a vector editor, where it's easy to create tens of thousands of AffineTransform instances inside the paint loop, when they are easily recycled and reused, and this showed itself to be a significant bottleneck in profiling. I've used the same pattern to reuse CharBuffer instances when processing log files line-by line. Basically the pattern is: You have some data structure which is expensive to create, and it is one you can completely reset the state on, rather than create a new one every time. So, you create a PhantomReference subclass that strongly references the object you want to recycle and reuse, whose referent is a thing that could be referencing the object; to track when it is safe to recycle an object, you either

  1. Return a facade for the object, that implements the same interface or something close enough (e.g. a CharSequence implementation that wraps a CharBuffer), and use that as the referent of your PhantomReference or
  2. Callers pass you a reference to themselves and so you can recycle an object when the caller goes out of scope

In other words, the pattern here you're asking the queue to tell you when every object that could know about some cached thing is gone, so you can make it available for reuse to another caller.

Corelative answered 25/4, 2021 at 3:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.