Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
140 views
in Technique[技术] by (71.8m points)

java - Why should I use Weak/SoftReference with ReferenceQueue argument?Why cannot I poll original reference and check if null?

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 ?

P.S.

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...