Deadlock while removing PhantomReference from ReferenceQueue
Why does the following cause a deadlock? Tested under 1.5 and 1.6. Why garbage collector doesn't (eventually) enqueue the reference? Anyhow, the reference queue doesn't seem to have a clue, and waits indefinitely...
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
publicclass NewMain{
publicstaticvoid main(String[] args)throws Exception{
Object o =new Object();
ReferenceQueue q =new ReferenceQueue();
PhantomReference ref =new PhantomReference(o, q);
o =null;
//System.gc(); // <-- if this is commented ...
ref = (PhantomReference)q.remove();// ... then this creates a deadlock!
System.out.println("done");
}
}
Instead of System.gc(), I can achieve a similar effect using something like:
for (int i = 0; i < 100000000; i++)new Object().toString();
Any ideas?
Message was edited by:
ounos
[1710 byte] By [
ounosa] at [2007-11-26 13:32:34]

# 1
The method remove() blocks - as documented.It that what you mean by 'deadlock'?
# 2
Indeed it blocks as documented, but it does so indefinitely, even if the object is obviously reclaimable, and the queue should pick up the reference sometime in the future.
Maybe this is an artifact of the gc implementation, for instance it could think it is ok not to collect some memory if the program doesn't actively ask for more. If it is so, then this example proves this implementation to be dangerous.
Can anybody verify that this behavior doesn't only occur to me?
-edit- Please note how System.gc() actually changes the outcome of the program, not merely runtime performance. I think this is not acceptable at all, it should be a side-effect (regarding what the user program can see) free method.
# 3
Use remove(timeout) if you don't want it block indefinitely http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ref/ReferenceQueue.html#remove(long)
# 4
AjaySingh516, the problem is that no matter how much time I wait, the queue doesn't get the reference that it should...
# 5
> Maybe this is an artifact of the gc implementation, for instance it
could think it is ok not to collect some memory if the program doesn't
actively ask for more. If it is so, then this example proves this
implementation to be dangerous.
May be the object is not put on the collection queue (check if
WeakReference helps). I know one library that is doing it very well,
commons-httpclient. Look at
http://jakarta.apache.org/commons/httpclient/xref/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.html
# 6
The behavior I describe is identical for weak and phantom references. (For soft references I can't easily reproduce it, as System.gc() will ignore the soft-reachable object, as there is plenty of memory).
import java.lang.ref.*;
public class NewMain {
public static void main(String[] args) throws Exception {
Object o = new Object();
ReferenceQueue q = new ReferenceQueue();
Reference[] refs = { new WeakReference(o, q), new PhantomReference(o, q) };
o = null;
//System.gc(); // if this is not commented, the queue gets both references
Reference ref = null;
do {
ref = q.remove(1000); // If no System.gc(), always returns null!
System.out.println("got ref: " + ref);
} while (ref == null);
}
}
# 7
Your original NewMain compiles to the following bytecode: 0:new#2; //class java/lang/Object
3:dup
4:invokespecial#1; //Method java/lang/Object."<init>":()V
7:astore_1
8:new#3; //class java/lang/ref/ReferenceQueue
11: dup
12: invokespecial#4; //Method java/lang/ref/ReferenceQueue."<init>":()V
15: astore_2
16: new#5; //class java/lang/ref/PhantomReference
19: dup
20: aload_1
21: aload_2
22: invokespecial#6; //Method java/lang/ref/PhantomReference."<init>":(Lj
ava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V
25: astore_3
26: aconst_null
27: astore_1
28: aload_2
29: invokevirtual#7; //Method java/lang/ref/ReferenceQueue.remove:()Ljava
/lang/ref/Reference;
32: checkcast#5; //class java/lang/ref/PhantomReference
35: astore_3
36: getstatic#8; //Field java/lang/System.out:Ljava/io/PrintStream;
39: ldc#9; //String done
41: invokevirtual#10; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;)V
44: return
However, I don't believe that the VM spec would prohibit JITs from optimising away instructions 26 and 27. As a general rule, if you're playing with reference types you should probably assume that a reference to an object could still be on the stack until the method in which it was created returns or throws an exception up the stack.
# 8
Thanks for the feedback, YAT_Archivist . That sounds reasonable. Though, it seems like the garbage collector declines to run when not needed, even if no other thread is running. That's the conclusion I drew from my last attempt trying to see the object getting reclaimed (made the object reference public, even volatile, and let the stack into which the object was created, die; these should pretty much deny optimization tricks)
import java.lang.ref.*;
public class NewMain {
public static void main(String[] args) throws Exception {
new NewMain();
}
public volatile Object o;
public NewMain() throws Exception {
o = new Object();
ReferenceQueue q = new ReferenceQueue();
Reference[] refs = { new WeakReference(o, q), new PhantomReference(o, q) };
o = null;
new Work(q).start();
}
}
class Work extends Thread {
private ReferenceQueue q;
Work(ReferenceQueue q) {
this.q = q;
}
public void run() {
try {
Reference ref = null;
do {
ref = q.remove(1000); //still, always returns null
System.out.println("got ref: " + ref);
} while (ref == null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
What a lazy gc that is!
# 9
> What a lazy gc that is!
You do want your VM to run in a multi-process OS and not to deny your other apps a shot at the processor, don't you?
You could try concurrent GC - see http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html for a full list of the options available with 1.5. There may be a similar doc for 1.6 somewhere, but I don't know whether anything changed.
# 10
When I am testing the gc I like to turn on -Xincgc.
Also you may want to consider creating a situation where the GC actually needs to find objects like limiting the heap size and making your objects huge. Otherwise there is no point in the GC wasting time to reclaim one lonely object that hardly uses any space at all. And GC will typically be designed not to do so.
Furthermore, its a bit difficult for the GC to detect reclaimable objects that were used by the current thread local memory. So for the GC to reclaim this object it requires more than a casual examanation. Thus the GC does not catch these on a first pass typically.
The GC is most aggressive in clearing phantom references, then weak references, then finally soft references. Of course, no phantom can be cleared as long as there is a soft or weak against the same object, and no weak can be cleared as long as there is a soft against the same object.