ReferenceQueue的含义

Meaning of ReferenceQueue

我试着去理解classReferenceQueue

它是

的可选构造函数参数
SoftReference

WeakReference

也是PhantomReference的强制参数。

根据我看过的资料,我可以写一些论文

a) 对于 PhantomReference 方法总是 returns null

b) 对于 Phantom 引用:
1. gc检测到对象可以从内存中删除
2.将对象的引用放入ReferenceQueue
当我们调用 clear 或 link 从队列中引用时变得不可访问并且 gc 看到 3. 完成方法调用
4.释放内存
对于 weak/soft 参考:
1. gc检测到对象可以从内存中删除
2. 完成方法调用
3.释放内存
4. 引用放入队列的对象

  1. 我什么时候可以将第二个参数传递给 XXXReference 构造函数?
  2. 我可以获得哪些帮助?
  3. 为什么 PhantomReference 没有 ReferenceQueue 的构造函数?
  4. ReferenceQuee 的获取方法 returns 总是 null 的原因是什么?

1) When can I pass second argument to XXXReference constructor?

可以随时进行。当您需要在引用被破坏时对其进行处理时,您应该这样做。

2) Which help I can get?

我不明白这个问题

3) Why PhantomReference has not constructor without ReferenceQueue ?

PhantomReference 的目的是 更灵活 替代常规终结。但是,为了使其工作,引用 必须 入队,最终替换代码才能工作。 (无法处理未入队的 PhantomReference。)

相比之下,SoftReferenceWeakReference 对象通常不需要排队就很有用。

4) What the reason to have ReferenceQueue which get methods returns null always?

ReferenceQueue API 没有 get() 方法,所以我猜你说的是 PhantomReference API。之所以有 get() 方法是为了与超类兼容。 get()定义为returnnull的原因如下:

"In order to ensure that a reclaimable object remains so, the referent of a phantom reference may not be retrieved: The get method of a phantom reference always returns null."

(参见javadoc。)

换句话说,这样做是为了"resurrect" 无法访问所指对象。

更新

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

虚引用的区别在于get()方法alwaysreturnsnull。 (所以 javadoc 中的解释是......没有说服力。)

也许,下面的程序有点帮助:

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

由于 multi-threading,前两条消息的顺序有时会有所不同。并且有时,幻象引用在三个轮询后被报告入队,表明它花费了超过指定的 100 毫秒。

重点是

  • 软引用和弱引用在开始终结之前或之后被清除并入队
  • 虚引用在完成后入队,假设对象没有泄漏finalize方法,否则在对象再次变得不可访问后入队
  • (non-trivial) finalize() 方法的存在导致需要至少一个额外的垃圾收集周期来检测对象不可访问或幻象可再次访问

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

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

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

值得注意的是,幻象引用不会被自动清除,这意味着它需要另一个垃圾收集周期,直到对象的内存真正可以被重用,所以上面的例子至少需要三个周期 non-trivial finalize() 方法和两个不用。 Java 9 将改变这一点,自动清除幻影引用,因此在上面的示例中,将需要两个周期完成,一个不完成,直到内存真正可以回收。好吧,准确地说,在这个简单的例子中,对象的内存将永远不会被回收,因为程序在此之前就终止了。


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

实际应用程序经常使用参考 class 的子 class 来向它们添加更多信息。这就是 WeakHashMap.Entry 发生的情况,它扩展了 WeakReference 并记住了哈希码和值。清理可以在普通地图操作中完成,不需要任何线程同步。使用 finalize() 方法是不可能的,除了 map 实现无法将 finalize() 方法推入键的 class 之外。

这意味着“比定稿更灵活”。

WeakHashMap 演示了 get() 方法的用途。只要键没有被收集,它就会被报告为在地图中,并且可以在遍历所有键或条目时检索到。

PhantomReference.get() 方法已被覆盖为始终 return null 以防止应用程序可以复活排队引用的引用。这是“幻影引用不会自动清除”规则的直接后果。该规则本身是有问题的,其初衷是在黑暗中。虽然规则即将在下一个 Java 版本中更改,但恐怕 get() 将继续始终 return null 以向后兼容。