Meaning of ReferenceQueue - java

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
When can I pass second argument to XXXReference constructor?
Which help I can get?
Why PhantomReference has not constructor without ReferenceQueue ?
What the reason to have ReferenceQuee which get methods returns null always?

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.

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

Related

Java WeakReference is still holding valid reference when referent is no longer valid

I'm confused with this program:
class Point {
private final int x;
private final int y;
}
public class App
{
WeakReference<Point> newPoint() {
Point referent = new Point();
return new WeakReference<Point>(referent); // after return, stack parameter referent is invalid.
}
public static void main( String[] args ) {
App a = new App();
WeakReference<Point> wp = a.newPoint(); // wp is hold valid or invalid reference?
System.out.println(wp.get()); // not null
}
}
I knew that if weak reference is pointing to an object that's no longer alive, its get() should return null. But in my code, seems its still alive.
Where did I get wrong?
I knew that if weak reference is pointing to an object that's no longer alive, its get() should return null. But in my code, seems its still alive.
Your understanding is imprecise, especially where it relies on the idea of aliveness. Reference objects in general and WeakReference objects in particular are not directly concerned with any of the senses of aliveness that I recognize. Rather, they are concerned with reachability.
The API docs for java.lang.Reference#get() (which is not overridden by WeakReference) say this:
Returns this reference object's referent. If this reference object has been cleared, either by the program or by the garbage collector, then this method returns null.
Note well that the condition for get() returning null is rather specific: the reference object has been cleared. This is achieved for a given instance by invoking that instance's clear() method. As the doc indicates, this may be done by the garbage collector or by the application.
Among the key differences between Reference subclasses is the conditions under which the garbage collector will perform such clearing. For WeakReferences, the API docs say:
Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object [...].
Thus, until the garbage collector determines that a given object is (only) weakly reachable, it will not clear weak references to that object. The garbage collector probably does not run at all during the brief run of your small program, and if it did run, it would be surprising for it to be timed correctly to observe the Point in question to be weakly reachable before the reference's get() method is invoked.
You could try forcing a GC run by invoking System.gc() at the appropriate place. I anticipate that doing so will result in the weak reference being cleared. That would be for demonstrative purposes only, however. Generally speaking, you should rely on Java to perform GC when appropriate, not force it.

Can the finalize() method be called twice if the garbage collector retains memory twice in the same code?

I found it in many places that the finalize() method in java is called when the garbage collector or System.gc() has successfully retained the memory consumed by the redundant object with no more references to it. Also found that this method is called not more than a single time. I am not new to java but also not pretty much experienced. I may have a wrong understanding of it but let's say a piece of code
public class Solution {
#Override
protected void finalize(){
System.out.print("method called");
}
public static void main(String... args){
Solution obj1= new Solution();
Solution obj2 = new Solution();
Solution obj3 = new Solution();
System.gc();
obj1=obj2;
System.gc();
obj3=null;
System.gc();
}
}
Here, the finalize method is called twice because the memory heap becomes eligible for garbage cleaning two times. So, I am a bit confused whether I know the whole thing right or if it is supposed to behave the way it's behaving.
No. The finalize() method will only be called once by the GC on an object. The JVM sets a flag in the object header (I think) to say that it has been finalized, and won't finalize it again.
The javadoc states this explicitly:
" The finalize method is never invoked more than once by a Java virtual machine for any given object. "
Of course, there is nothing to stop an object method from calling this.finalize() any number of times.
Note that finalize() is deprecated in Java 9 and later for reasons stated in the javadoc. It is recommended that you switch to using one of the following instead:
AutoCloseable + try with resources
Cleaner
PhantomReference
Someone commented thus:
finalize() is called for every Object that is collected.
This is not true for a couple of reasons.
The javadoc explicitly states that there are no guarantees that finalize will ever be called. The thing that is guaranteed is that it will be called (once) before an object's storage is reclaimed. That is a weaker statement than the statement that the comment makes.
One scenario where garbage collected objects may not be finalized is if the JVM exits soon after a GC run.
Another (pathological) scenario occurs when a classes finalize method never returns1. When an instance of that class is finalized, the finalizer thread will get stuck. When all finalizer threads are stuck in that way, no more finalizable objects can be finalized2.
If the Object::finalize is not overridden in a class, the JVM will skip the finalization step for that class.
1 - This could be due to an infinite loop, or because the finalize() method gets stuck waiting on a lock or waiting for an internal or external "event" that never happens. Note also that "never" could mean "not for a long time" in this context. The overall impact can be the same.
2 - The objects will sit in the finalization queue indefinitely. This is a memory leak.

Java PhantomReference vs finalize()

I've been reading this article about PhantomReference https://www.baeldung.com/java-phantom-reference and simplified sample code found there:
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
Object object = new Object();
PhantomReference<Object> phantomReference = new PhantomReference<>(object, referenceQueue);
object = null;
System.gc();
Thread.sleep(1_000);
System.out.println("isEnqueued() after GC: " + phantomReference.isEnqueued());
Reference reference = referenceQueue.poll();
if(reference != null) {
System.out.println("isEnqueued() after poll(): " + phantomReference.isEnqueued());
}
}
Here is output:
isEnqueued() after GC: true
isEnqueued() after poll(): false
So everything is working as expected, strong reference to object is set to null which is detected by GC and phantom reference is added to the queue.
Now in that article they say: "The Garbage Collector adds a phantom reference to a reference queue after the finalize method of its referent is executed. It implies that the instance is still in the memory."
So I wanted to make a test and override finalize method like:
Object object = new Object() {
#Override
protected void finalize() throws Throwable {
System.out.println("finalize()");
}
};
But then output is different, phantom reference is not added to the queue anymore:
finalize()
isEnqueued() after GC: false
Could somebody explain why after this change output is different and how to change this code so phantom reference would be added to queue?
I've been testing this on JDK 8 and 11, same results on both platforms.
The statement “The Garbage Collector adds a phantom reference to a reference queue after the finalize method of its referent is executed.” is a bit sloppy at best.
You should refer to the specification:
If the garbage collector determines at a certain point in time that the referent of a phantom reference is phantom reachable, then at that time or at some later time it will enqueue the reference.
Whereas the linked definition of “phantom reachable” states:
An object is phantom reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.
So an object is phantom reachable “after the finalize method of its referent is executed” if only referenced by phantom references and hence will be enqueued after that but not immediately. Since an object is strongly reachable during the execution of its finalize() method, it takes at least one additional garbage collection cycle to detect that it became phantom reachable. Then, “at that time or at some later time” it will get enqueued.
If you change the program to
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
Object object = new Object() {
#Override
protected void finalize() throws Throwable {
System.out.println("finalize()");
}
};
PhantomReference<Object> phantomReference=new PhantomReference<>(object, referenceQueue);
object = null;
System.gc();
Thread.sleep(1_000);
System.gc();
Thread.sleep(1_000);
System.out.println("isEnqueued() after GC: " + phantomReference.isEnqueued());
Reference reference = referenceQueue.poll();
if(reference != null) {
System.out.println("isEnqueued() after poll(): " + phantomReference.isEnqueued());
}
You will most likely see the desired output, but it has to be emphasized that there are no guarantees that the garbage collector will actually run when you call System.gc() or that it completes in a particular amount of time when it runs or that it will find all unreachable objects within a particular cycle. Further, the enqueuing happens asynchronously after the gc cycle so even by the time the garbage collector completed and detected a special reachability state, an additional amount of time may pass before the reference gets enqueued.
Note that the sentence “It implies that the instance is still in the memory.” also doesn’t get it right, but in this case, it’s based on a misunderstanding that was even on the Java core developer’s side.
When the API was created, a sentence was added to the specification which you will find even in the Java 8 version:
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.
This may lead to the naïve assumption that the object still has to be in memory, but The Java® Language Specification states:
Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable.
Simply said, the memory of objects may get reclaimed earlier, if the behavior of the program does not change. This applies especially to scenarios, where the application can not use the object at all, like with the phantom reference. The behavior of the program wouldn’t change if the object is not in memory anymore, so you can not assume that it actually is.
This leads to the question, why the rule that phantom references are not cleared was added to the spec at all. As discussed in this answer, that question was brought up and could not be answered at all. Consequently, this rule has been removed in Java 9 and phantom reference are cleared when enqueued, like weak and soft reference. That’s an even stronger reason not to assume that the object is still in memory, as now even non-optimizing environments can reclaim the object’s memory at this point.

Test a weak reference before using it java

In a multithreaded Android project, I'm seeing code like this:
final WeakReference<MyClass> myClassObjectWeakRef =
new WeakReference<MyClass>(aMyClassObject);
...then somewhere else:
if (myClassObjectWeakRef.get() != null) {
myClassObjectWeakRef.get().someMethod();
}
I'm pretty sure there is a possible race condition between the check and the use of the reference, if the last strong reference to the object is released between the two in another thread, but I can't find any documentation or anyone which/who can confirm this better than with a "you're probably right".
I would think the only right way to test & use a weak reference is done like this:
MyClass myObject = myClassObjectWeakRef.get();
// we now have a strong reference, or null: standard checks apply.
if (myObject != null) {
myObject.someMethod();
}
I'm very confident that the second method is 100% safe, but I wonder if there is some Java/compiler sugar/magic that I don't know about, which would make the first method safe.
So, is the first method 100% safe, or not?
The first method is definitely unsafe. Each call to get is independent. There is nothing preventing the GC from clearing the weakly reachable object after the first get and before the second.
The javadoc states
Suppose that the garbage collector determines at a certain point in
time that an object is weakly reachable. At that time it will
atomically clear all weak references to that object and all weak
references to any other weakly-reachable objects from which that
object is reachable through a chain of strong and soft references.
That can be at any point in time. Calling get(), which (potentially) pushes a reference to the object on the stack temporarily makes the object strongly reachable (it's on a thread's stack), but that reach-ability disappears the moment the comparison with null finishes. After that moment, the GC can determine that the object is weakly reachable and clear its reference. You'd then get a NullPointerException.
Use your second method. But note that by assigning it to a variable, you are making the referenced object strongly reachable.

Finalize() cleanup vs. Garbage Collector removing an object from memory

I was reading about the finalize() method and was curious:
What is the difference between the task of cleaning up objects ( setting them to NULL ) in finalize, and removing an object from memory?
What is the difference between the task of cleaning up objects (
setting them to NULL ) in finialize
setting to null removes ONE reference to the object.
if NO more references to an object exists, the garbage collector is allowed (not required) to remove the object
and removing an object from memory?
there is NO explicit way in java to remove (destroy, delete) an object.
The garbage collector will do it when he likes.
Especially the time from removing the last reference to remove/destroy the object is indefinite
There is NO need to set references to null in finalize method.
when the garbage collector call finalize the objects and its references will gone soon anyway.
I never wrote an own finalize method during my very long java experience.
The rare occasion in which it make sense to wrote an own finalize method appear if your object is dealing with os-resources. However, in general you use standard packages for os accesss
You don't "clean up" an object when you set it to null, you're just setting the reference to null, consider:
Object a = new Object();
Object b = a;
a = null;
System.out.println(b);
Once an object loses all references, it will be collected on the next GC pass. Finalize is a method that gets called when this happens, and you should avoid using it.
Just don't keep extra references around and let the GC do it's job.
finalize() is called by garbage collector when an object has no more references. You can override it and best practice is to use it in a try-catch-finally block to free non java resources like files. Anyway if you use it this way you should also call super.finalize() to ensure class hierarchy finalization.
This method is always for advanced use and shouldn't be used in normal production code. Free your resources in finally clauses in methods using those resources.

Categories

Resources