Meaning of ReferenceQueue
Asked Answered
S

2

2

I try to understand class ReferenceQueue

It is optional constructor argument for

SoftReference

and

WeakReference

Also it is mandatory argument for PhantomReference.

According information I have read I can write some thesises

a)for PhantomReference method get always returns null

b) for Phantom references:
1. gc detect that object can be deleted from memory
2. reference to the object puted to the ReferenceQueue
when we invoke clear or link to reference from queue becaom unreachable and gc see that 3. finalize methods invokes
4. free memory
for weak/soft references:
1. gc detect that object can be deleted from memory
2. finalize methods invokes
3. free memory
4. reference to the object puted to the queue

  1. When can I pass second argument to XXXReference constructor?
  2. Which help I can get?
  3. Why PhantomReference has not constructor without ReferenceQueue ?
  4. What the reason to have ReferenceQuee which get methods returns null always?
Shanta answered 29/12, 2016 at 11:36 Comment(0)
D
5

Maybe, the following program helps a bit:

public class SimpleGCExample {
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Object> queue=new ReferenceQueue<>();
        SimpleGCExample e = new SimpleGCExample();
        Reference<Object> pRef=new PhantomReference<>(e, queue),
                          wRef=new WeakReference<>(e, queue);
        e = null;
        for(int count=0, collected=0; collected<2; ) {
            Reference ref=queue.remove(100);
            if(ref==null) {
                System.gc();
                count++;
            }
            else {
                collected++;
                System.out.println((ref==wRef? "weak": "phantom")
                                  +" reference enqueued after "+count+" gc polls");
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalizing the object in "+Thread.currentThread());
        Thread.sleep(100);
        System.out.println("done finalizing.");
    }
}

On my system, it prints

weak reference enqueued after 1 gc polls
finalizing the object in Thread[Finalizer,8,system]
done finalizing.
phantom reference enqueued after 2 gc polls

or

finalizing the object in Thread[Finalizer,8,system]
weak reference enqueued after 1 gc polls
done finalizing.
phantom reference enqueued after 2 gc polls

The order of the first two messages occasionally differs due to the multi-threading. And sometimes, the phantom reference is reported to be enqueued after three polls, indicating that it took more than the specified 100 milliseconds.

The key point is

  • soft and weak references are cleared and enqueued before or right when starting finalization
  • phantom references are enqueued after finalization, assuming that the object has not leaked the finalize method, otherwise they are enqueued after the object has become unreachable again
  • the presence of a (non-trivial) finalize() method causes the need of at least one additional garbage collecting cycle to detect that the object is unreachable or phantom reachable again

Since more than 99% of all objects don’t need finalization, all JVM vendors are strongly encouraged to detect when finalize() has not been overridden or is “trivial”, i.e. an empty method or a sole super.finalize() call. In these cases, the finalization step should be elided. You can easily check that this optimization happens in your JVM, by removing the finalize() method in the above example. Then it prints

weak reference enqueued after 1 gc polls
phantom reference enqueued after 1 gc polls

Since both are enqueued at once and retrieved in an arbitrary order, the order of the two messages may differ. But they are always both enqueued after one gc cycle.

It’s worth noting that the fact, that phantom references are not automatically cleared, implies that it takes another garbage collection cycle until the object’s memory really can be reused, so the above example requires at least three cycles with the non-trivial finalize() method and two without. Java 9 is going to change that, clearing phantom references automatically, so in the above example it will take two cycles with finalization and one without until the memory really can be reclaimed. Well, to be precise, in this simple example the object’s memory will never be reclaimed as the program terminates before that can happen.


The code above also demonstrates one of the intended use cases of the Reference API. We can use it to detect when an object’s reachability changed within code under our full control, e.g. using a loop within the main method. In contrast, finalize() may be called by a different, unspecified thread at an arbitrary time. The example also shows that you can draw information from the reference object without needing the get() method.

Practical applications often use subclasses of the reference classes to add more information to them. This is what happens with WeakHashMap.Entry which extends WeakReference and remembers the hash code and value. The cleanup can be done within the normal map operations, not needing any thread synchronization. This would not be possible with a finalize() method, besides the fact that the map implementation can’t push a finalize() method into the class of the keys.

This is meant with the “more flexible than finalization” term.

The WeakHashMap demonstrates how the get() method can be useful. As long as the key hasn’t been collected, it will be reported as being in the map and can be retrieved when iterating over all keys or entries.

The PhantomReference.get() method has been overwritten to always return null to prevent that an application can resurrect the referent of an enqueued reference. This is a direct consequence of the “phantom references are not automatically cleared” rule. That rule itself is questionable and it’s original intention lies in the dark. While the rule is about to be changed in the next Java version, I’m afraid that get() will continue to always return null to be backwards compatible.

Dhruv answered 2/2, 2017 at 9:49 Comment(12)
brrr. About first output: I thought that if I got weakRef from queue then I can count that memory already reclaimed. But according your output, finalize executes after engueuing for weakRef.Also I know that finalize can be invoked on alive object. For phantom references all should be vise verca. You broke my brain. I don't understand allShanta
A finalize() method should only do necessary cleanup without resurrecting the instance, hence, it’s reasonable to assume that the object’s lifetime is over, when the weak reference has been enqueued. For most objects, not needing finalization and having no phantom reference pointing to it, the assumption will always be true. A particular finalize() implementation could render it wrong, but that’s a misbehavior of that particular finalize() implementation. If you want to be absolutely sure that finalization has been completed, you need a phantom reference.Dhruv
I guess, your confusion stems from this wrong article you referred to earlier. As already said here, it’s contradicting documentation and actual behavior of the implementation. Attempts to implement like described in that article wouldn’t work anyway. If we clear a reference first, then let the finalizer run and try to enqueue afterwards, how should a JVM know that it has to enqeue particular references when they are already cleared?Dhruv
Do we have ordering of enqueuing and finalize call for each reference?Shanta
enqueuing of which references? Technically, weak, soft and finalizer references are enqueued at the same time, then, the finalizer thread(s) will poll their queue concurrently to the rest of the application. Like in my example app, where the main thread also polls its queue and the first two messages (“weak enqueued” and “finalizing”) are printed in an unpredictable order. If you want to enforce an order, you have to use thread synchronization. I never needed it anyway. The enqueuing of the phantom references happens after the finalization.Dhruv
what the finalizer reference is it phantom reference?Shanta
phantom reference enqueued after finalization/ I really don't understand why. java doc says Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueuedShanta
A finalizer reference is an additional special, non-public reference type. I recommend reading the answer I gave you to this question. It explains the background. A finalizer reference is enqueued at the same time, soft and weak references are enqueued, but, of course, not cleared as the referent is needed to invoke its finalize() method. A phantom reference is a different kind of reference, which is enqueued after finalization and there is no “why” to understand. It’s so, because the API designers said so, as it’s the primary purpose of it.Dhruv
but from documentation: Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueuedShanta
So what? I don’t get why you keep repeating that sentence. Phantom references are enqueued after finalization and they are not automatically cleared. That’s two different facts. If you still have any question, ask.Dhruv
phantom references are enqueued after finalization, assuming that the object has not leaked the finalize method Where have you find that it is truth ?*(Phrase from documentation?) Is it still correct in java 9 ?Shanta
java 11 prints: weak reference enqueued after 1 gc polls finalizing the object in Thread[Finalizer,8,system] done finalizing. phantom reference enqueued after 2 gc pollsShanta
C
1

1) When can I pass second argument to XXXReference constructor?

You can do it whenever you like. You should do it whenever you need the references to be processed as they are being broken.

2) Which help I can get?

I don't understand this questiom

3) Why PhantomReference has not constructor without ReferenceQueue ?

The purpose of PhantomReference is to be a more flexible alternative to regular finalization. However, in order for that to work, the references must be enqueued for the finalization-replacing code to work. (A PhantomReference that was not enqueued could not be processed.)

By contrast SoftReference and WeakReference objects are often useful without being enqueued.

4) What the reason to have ReferenceQueue which get methods returns null always?

The ReferenceQueue API doesn't have a get() method, so I guess you are talking about the PhantomReference API. The reason that there is a get() method is for compatibility with the superclass. The reason that get() is defined to return null is as follows:

"In order to ensure that a reclaimable object remains so, the referent of a phantom reference may not be retrieved: The get method of a phantom reference always returns null."

(See the javadoc.)

In other words, it is done to make it impossible to "resurrect" the referent.

UPDATE

In fact, all of the Reference classes will clear the referent before enqueuing a Reference. (Actually, the GC itself does this directly.) Application code that pulls references from a ReferenceQueue cannot use the get() method to identify the (now deleted!) referent. They must do it some other way; e.g. based on the identity of the Reference object.

What distinguishes a phantom reference is that the get() method always returns null. (So the explanation in the javadoc is ... unconvincing.)

Confetti answered 29/12, 2016 at 11:59 Comment(5)
As I understood when phantom reference turned out to the ReferenceQueue then object already ate by GC. Thus How can I understand that object was collected. method poll returns nullShanta
"How can I understand that object was collected. method poll returns null". I think you are confusing the Reference and ReferenceQueue APIs again. The poll() method is in the ReferenceQueue API. It returns null if the queue is empty; i.e. if no phantom references were enqueued / no referent objects were deleted recently. But poll() not defined to always return null. It is the get() that always returns null.Confetti
yes, I a bit cinfusing. Is it truth: for Phantom references: 1. gc detect that object can be deleted from memory 2. reference to the object puted to the ReferenceQueue ;when we invoke clear or link to reference from queue become unreachable and gc see that: 3. finalize methods invokes 4. free memory for weak/soft references: 1. gc detect that object can be deleted from memory 2. finalize methods invokes 3. free memory 4. reference to the object puted to the queueShanta
The key point is that for weak and soft references, the reference is automatically cleared when enqueued whilst for phantom references, it is not. But it is still not intended to fetch the non-null referent, hence get() is overwritten to always return null instead of the referent. So for all enqueued references, get() will return nullDhruv
@Dhruv Looks like if I have not reference to PhantomReference object but I have reference to referenceQueue then object can be collected after appearence in queue. Is it truth?Shanta

© 2022 - 2024 — McMap. All rights reserved.