为什么我应该在ReferenceQueue参数中使用Weak / SoftReference?为什么我不能轮询原始引用并检查是否为null? [英] Why should I use Weak/SoftReference with ReferenceQueue argument?Why cannot I poll original reference and check if null?

查看:251
本文介绍了为什么我应该在ReferenceQueue参数中使用Weak / SoftReference?为什么我不能轮询原始引用并检查是否为null?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

正如我理解的工作用例,它检测何时收集对象以及内存已经空闲以供弱/软引用使用,它轮询该队列,并且当引用出现在队列中时,我们可以确定内存是空闲的。

  WeakReference ref = new WeakReference(new Object())

为什么我不能轮询 ref 并检查它是否为空?

PS



根据评论中提供的链接:


如果垃圾收集器发现一个对象弱可达,
发生以下情况:
1. WeakReference对象的引用字段将
设置为null,从而使其不再引用堆对象。

2.WeakReference引用的堆对象是
声明可定制的。
3.当堆对象的finalize()方法运行
并释放其内存时,WeakReference对象将添加到它的
ReferenceQueue中(如果存在)。


因此,如果本文写下真相,并且这些步骤按照weakreference的顺序变为null,但对象只会在第3步添加到队列中。



是真的吗?



这是为什么?



让研究代码: / p>

工作规范示例:

  public class TestPhantomRefQueue {

public static void main(String [] args)
throws InterruptedException {

Object obj = new Object();
final ReferenceQueue队列=新的ReferenceQueue();

WeakReference pRef =
WeakReference(obj,queue);

obj = null;
$ b $ new Thread(new Runnable(){
public void run(){
try {
System.out.println(等待GC);

//这会阻塞直到GCd
引用prefFromQueue;
while(true){
prefFromQueue = queue.remove();
if( prefFromQueue!= null){
break;
}
}
System.out.println(Referenced GC'd);
System.out.println( (例外e){
e.printStackTrace();
}
}
;


} })。开始();

//等待第二个线程启动
Thread.sleep(2000);

System.out.println(调用GC);
System.gc();
}
}

此代码输出:

 等待GC 
调用GC
引用GC'd
null



好的,我理解它为什么能起作用。

允许更改一些代码:

  public class TestPhantomRefQueue {

public static void main(String [] args)
throws InterruptedException {

Object obj = new Object();
final ReferenceQueue队列=新的ReferenceQueue();

WeakReference pRef =
WeakReference(obj,queue);

obj = null;
$ b $ new Thread(new Runnable(){
public void run(){
try {
System.out.println(等待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();

//等待第二个线程启动
Thread.sleep(2000);

System.out.println(调用GC);
System.gc();


这个变体挂在while循环和输出中:等待GC
调用GC

请解释这种行为。

解决方案



在你的代码中,行

.get()== null

会分配 pRef.get()到方法 run()的临时插槽。垃圾收集器将栈中活动帧中的所有槽/本地变量视为GC根。无意中你正在创建对你的对象的强引用,所以它不会被清除。



我修改了版本,我已经将 pRef.get() code>嵌套方法。一旦执行从方法返回它的框架被处置,所以引用对象仍然是为了防止GC收集它。



因为,如果JVM会重新编译 run()方法并内联 isRefernceCollected(pRef)调用,它可能会再次中断。



总之,引用队列为您提供确定性和高效的方式来处理引用。共享池可以工作,但它很脆弱,并且依赖于通过javac和JVM JIT进行代码编译。



已修改的代码段。

  public class TestPhantomRefQueue {

public static void main(String [] args)
throws InterruptedException {

Object obj = new Object();
final ReferenceQueue队列=新的ReferenceQueue();

WeakReference pRef =
WeakReference(obj,queue);

obj = null;
$ b $ new Thread(new Runnable(){
public void run(){
try {
System.out.println(等待GC);

while(true){
if(isRefernceCollected(pRef)){
Thread.sleep(100);
break;
}
}
System.out.println(Referenced GC'd);
System.out.println(pRef.get());


} catch (例外e){
e.printStackTrace();
}
}

保护布尔isRefernceCollected(final WeakReference pRef){
return pRef.get ()== null;
}
})。start();

//等待第二个线程启动
Thread.sleep(2000);

System.out.println(调用GC);
System.gc();
}
}

输出

 等待GC 
调用GC
引用GC'd
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.

解决方案

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

这篇关于为什么我应该在ReferenceQueue参数中使用Weak / SoftReference?为什么我不能轮询原始引用并检查是否为null?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆