ReferenceQueue的含义

时间:2016-12-29 11:36:37

标签: java garbage-collection weak-references phantom-reference

我尝试了解课程ReferenceQueue

它是

的可选构造函数参数
SoftReference

WeakReference

它也是PhantomReference的强制性参数。

根据我读过的信息,我可以写一些论文

a)对于PhantomReference方法,get始终返回null

b)中 用于幻影参考:
   1. gc检测到该对象可以从内存中删除
   2.引用参考问题的对象
   当我们调用clear或链接到队列中的引用因为无法访问而gc看到了    3.敲定方法调用
   4.免费记忆
弱/软参考:
   1. gc检测到该对象可以从内存中删除
   2. finalize方法调用
   3.免费记忆
   4.引用队列中的对象

  1. 我什么时候可以将第二个参数传递给XXXReference构造函数?
  2. 我能得到哪些帮助?
  3. 为什么PhantomReference没有ReferenceQueue的构造函数?
  4. 让参数返回null的ReferenceQuee的原因是什么?

2 个答案:

答案 0 :(得分:1)

  

1)我什么时候可以将第二个参数传递给XXXReference构造函数?

可以随时随地执行此操作。 应该在您需要处理引用时进行

  

2)我能得到哪些帮助?

我不明白这个问题

  

3)为什么PhantomReference没有没有ReferenceQueue的构造函数?

PhantomReference的目的是成为常规定稿的更灵活的替代方案。但是,为了使其工作,引用必须排队才能使终结替换代码起作用。 (无法排队的PhantomReference无法处理。)

相比之下,SoftReferenceWeakReference对象通常无需排队即可使用。

  

4)使用ReferenceQueue获取方法的原因是什么?

ReferenceQueue API没有get()方法,因此我猜你在谈论PhantomReference API。存在get()方法的原因是为了与超类兼容。 get()定义为返回null的原因如下:

  

"为了确保可恢复的对象保持不变,可能无法检索幻像引用的引用:幻像引用的get方法始终返回null。"

(参见javadoc。)

换句话说,它是为了让它不可能复活"指称。

<强>更新

事实上,所有Reference类都会在排队Reference之前清除指示对象。 (实际上,GC本身直接执行此操作。)从ReferenceQueue提取引用的应用程序代码无法使用get()方法来标识(现已删除!)指示对象。他们必须以其他方式做到这一点;例如基于Reference对象的身份。

幻像参考的区别在于get()方法总是返回null。 (所以javadoc中的解释是......难以令人信服。)

答案 1 :(得分:1)

也许,以下程序有点帮助:

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.");
    }
}

在我的系统上,它会打印

weak reference enqueued after 1 gc polls
finalizing the object in Thread[Finalizer,8,system]
done finalizing.
phantom reference enqueued after 2 gc polls

finalizing the object in Thread[Finalizer,8,system]
weak reference enqueued after 1 gc polls
done finalizing.
phantom reference enqueued after 2 gc polls

由于多线程,前两个消息的顺序偶尔会有所不同。有时候,据报道幻象参考在三次民意调查后被排队,表明它花了超过规定的100毫秒。

关键点是

  • 在开始最终确定之前或之后清除并排列软弱引用
  • 幻像引用在完成后排队,假设对象没有泄露finalize方法,否则它们在对象再次无法访问后排队
  • (非平凡)finalize()方法的存在导致需要至少一个额外的垃圾收集周期来检测对象无法访问或幻像可以再次访问

由于超过99%的对象不需要最终确定,因此强烈建议所有JVM供应商检测何时finalize()未被覆盖或“无关紧要”,即空方法或单super.finalize() 1}}打电话。在这些情况下,应省略最终确定步骤。通过删除上例中的finalize()方法,您可以轻松地检查JVM中是否发生了此优化。然后打印

weak reference enqueued after 1 gc polls
phantom reference enqueued after 1 gc polls

由于两者都被排队并以任意顺序检索,因此两条消息的顺序可能不同。但是他们总是在一个gc循环后排队。

值得注意的是,虚拟引用未被自动清除的事实意味着它需要另一个垃圾收集周期,直到对象的内存真的可以被重用,因此上面的示例需要至少三个循环的非平凡{ {1}}方法和两个没有。 Java 9将改变这一点,自动清除幻像引用,因此在上面的例子中,它将需要两个周期完成,一个没有,直到内存真的可以回收。好吧,确切地说,在这个简单的例子中,对象的内存永远不会被回收,因为程序会在可能发生之前终止。

上面的代码还演示了Reference API的一个预期用例。我们可以使用它来检测在完全控制下代码中对象的可达性何时发生变化,例如:在finalize()方法中使用循环。相反,main可以在任意时间由不同的,未指定的线程调用。该示例还显示您可以从参考对象中提取信息,而无需finalize()方法。

实际应用程序通常使用引用类的子类来向它们添加更多信息。这是get()扩展WeakHashMap.Entry并记住哈希码和值的情况。清理可以在正常的映射操作中完成,不需要任何线程同步。使用WeakReference方法无法做到这一点,除了地图实现无法将finalize()方法推送到密钥类中这一事实。

这意味着“比最终确定更灵活”一词。

finalize()演示了WeakHashMap方法如何有用。只要尚未收集密钥,它就会被报告为在地图中,并且可以在迭代所有密钥或条目时进行检索。

get()方法已被覆盖,以便始终返回PhantomReference.get(),以防止应用程序恢复入队引用的引用。这是“幻像引用未自动清除”规则的直接结果。这条规则本身是值得怀疑的,它的初衷是在黑暗中。虽然规则即将在下一个Java版本中进行更改,但我担心null将继续始终返回get()以向后兼容。