4

My question that sums it all up:

  • Can a strongly reachable Java PhantomReference stop its referent object's memory from being reclaimed by the Garbage Collector (GC)?

Details follow:

Callum posted this question as well but it is not answered straightforwardly. One response there refers to an article by Ethan Nicholas which seems to answer my question with a "No" but I'm not sure that is correct.

Based on my reading of the Java API I would have to answer my question with "Yes":

  • As long as PhantomReference.clear() is not called, and the PhantomReference instance itself is still strongly referred to, the referent object's memory will never be reclaimed and the referent will remain in a phantom reachable state.

To support this understanding I will quote the Java Docs:

  • "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."

For example, let's say I make a phantom reference and keep that instance in a List of PhantomReference. Then its referent falls from strongly reachable to phantom reachable.

If you take a look at com.google.common.base.internal.Finalizer.java, you will see the following code:

  private void cleanUp(Reference reference) throws ShutDown {
      ...

      /*
       * This is for the benefit of phantom references. Weak and soft
       * references will have already been cleared by this point.
       */
      reference.clear();

      ...
  }

I would prefer someone who is experienced with the subject to respond rather than doing a web search and providing me with links. Thanks!

Vahid Pazirandeh
  • 1,552
  • 3
  • 13
  • 29
  • have you considered simply testing it? a debugger, reflection or a heapdump can look at the references after GC. Or you can just force an OOME by holding onto very large objects. – the8472 May 28 '15 at 16:34

1 Answers1

2

You mixed two things up. The linked question is not about the referent, but the PhantomReference instance. A PhantomReference instance, like all reference objects, can get garbage collected like any other object as long as it has not been enqueued. This is specified in the package specification:

The relationship between a registered reference object and its queue is one-sided. That is, a queue does not keep track of the references that are registered with it. If a registered reference becomes unreachable itself, then it will never be enqueued. It is the responsibility of the program using reference objects to ensure that the objects remain reachable for as long as the program is interested in their referents.

But your question is about the referent. Also, the cited code is about dealing with references which were already enqueued and even retrieved from the queue.

At this place, the documentation you’ve cited applies. Up to and including Java 8, referents of reachable PhantomReferences are not automatically cleared, so the referent stays phantom reachable until either, the reference is cleared or becomes unreachable itself. So the cited code is correct in clearing the reference explicitly to allow early reclaiming, though, the difference only affects the duration of the cleanup method’s execution, as afterwards, the PhantomReference likely becomes unreachable itself.


But that’s not the end of the story. There is no clear reason why referents should stay phantom reachable instead of being reclaimed. After all, the cleanup method can’t access the referent anyway.

So Java 9 drops that rule and clears phantom references automatically like any other reference. So starting with Java 9, manual clearing already enqueued phantom references is unnecessary, but of course, doesn’t hurt, so old software will still work smoothly.


Regarding you example:

…let's say I make a phantom reference and keep that instance in a List of PhantomReference. Then its referent falls from strongly reachable to phantom reachable.

Being the referent of a PhantomReference reference alone is not sufficient to be phantom reachable. It also requires that there are no strong references and the object has been finalized, though finalization is practically skipped for most objects, as they don’t have a custom finalize() method. When there are soft references additionally to the phantom reference, it may depend on the configuration and memory needs, whether the referent will become phantom reachable.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • the least I can do is upvote here, I've only read about this, never used it, but it seems you are all over this one too! – Eugene Dec 22 '17 at 08:49
  • @Eugene: Neither did I use it in application code. In the really rare cases where it would have been appropriate, I used `WeakReference` instead, to avoid the performance implication of exactly that “not automatically cleared” issue. Since we never have non-trivial `finalize()` methods, there is no difference between these reference types to us. The reason, I’m familiar with this, is that it was nagging me since the release of jdk1.2 that I didn’t fully understand it. While the documentation still has room for improvements, Java 9 answered my last remaining question, why they are not cleared. – Holger Dec 22 '17 at 12:25
  • I agree with @Eugene - I haven't been deep into these areas for a while now and don't plan to so I can't validate anything stated. You seem all over this subject. Marking your post as the Answer. Thanks @Holger! :-) – Vahid Pazirandeh Dec 22 '17 at 20:53