As I understand working use case of detecting when object was collected and memory already free for weak/soft references it polling this queue and when reference appear in queue we can be sure that memory free.
WeakReference ref = new WeakReference (new Object())
Why cannot I poll ref
and check that it became null ?
according the link provided in comment:
If the garbage collector discovers an object that is weakly reachable, the following occurs: 1.The WeakReference object's referent field is set to null, thereby making it not refer to the heap object any longer.
2.The heap object that had been referenced by the WeakReference is declared finalizable. 3.When the heap object's finalize() method is run and its memory freed, the WeakReference object is added to its ReferenceQueue, if it exists.
Thus if this article write truth and these steps ordered weakreference becomes null after step but object adds to the queue only on 3th step.
Is it truth?
Is it cause why?
Lets research code:
working canonical example:
public class TestPhantomRefQueue {
public static void main(String[] args)
throws InterruptedException {
Object obj = new Object();
final ReferenceQueue queue = new ReferenceQueue();
final WeakReference pRef =
new WeakReference(obj, queue);
obj = null;
new Thread(new Runnable() {
public void run() {
try {
System.out.println("Awaiting for GC");
// This will block till it is GCd
Reference prefFromQueue;
while (true) {
prefFromQueue = queue.remove();
if (prefFromQueue != null) {
break;
}
}
System.out.println("Referenced GC'd");
System.out.println(pRef.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
// Wait for 2nd thread to start
Thread.sleep(2000);
System.out.println("Invoking GC");
System.gc();
}
}
this code output:
Awaiting for GC
Invoking GC
Referenced GC'd
null
Ok, I understand why it does work.
Lets change code a bit:
public class TestPhantomRefQueue {
public static void main(String[] args)
throws InterruptedException {
Object obj = new Object();
final ReferenceQueue queue = new ReferenceQueue();
final WeakReference pRef =
new WeakReference(obj, queue);
obj = null;
new Thread(new Runnable() {
public void run() {
try {
System.out.println("Awaiting for GC");
while (true) {
if (pRef.get() == null) {
Thread.sleep(100);
break;
}
}
System.out.println("Referenced GC'd");
System.out.println(pRef.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
// Wait for 2nd thread to start
Thread.sleep(2000);
System.out.println("Invoking GC");
System.gc();
}
}
this variant hangs in while loop and output:
Awaiting for GC
Invoking GC
Please explain this behaviour.
With little modification your code would produce anticipated result. See code snippet below.
In your code, line
pRef.get() == null
would assign pRef.get()
to temporary slot in frame of method run()
. Event after condition is calculated, slot is not clear automatically.
Garbage collector treat all slots/local variable in active frames on stack as GC roots. Unintentionally you are creating strong reference to your object so it wont be cleared.
I modified version, I have moved pRef.get()
to nested method. Once execution returns from method its frame is disposed, so to reference to object remains to prevent GC to collect it.
Of cause, if JVM would recompile run()
method and inline isRefernceCollected(pRef)
call, it may break again.
In summary, reference queues give you deterministic and efficient way to handle reference. Pooling can work but it is fragile and depends of code compilation by javac and JVM JIT.
Modified code snippet.
public class TestPhantomRefQueue {
public static void main(String[] args)
throws InterruptedException {
Object obj = new Object();
final ReferenceQueue queue = new ReferenceQueue();
final WeakReference pRef =
new WeakReference(obj, queue);
obj = null;
new Thread(new Runnable() {
public void run() {
try {
System.out.println("Awaiting for GC");
while (true) {
if (isRefernceCollected(pRef)) {
Thread.sleep(100);
break;
}
}
System.out.println("Referenced GC'd");
System.out.println(pRef.get());
} catch (Exception e) {
e.printStackTrace();
}
}
protected boolean isRefernceCollected(final WeakReference pRef) {
return pRef.get() == null;
}
}).start();
// Wait for 2nd thread to start
Thread.sleep(2000);
System.out.println("Invoking GC");
System.gc();
}
}
Output
Awaiting for GC
Invoking GC
Referenced GC'd
null