1

我试着理解课堂ReferenceQueue

它是可选的构造函数参数

SoftReference

WeakReference

它也是PhantomReference.

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

a) 对于 PhantomReference 方法 get 总是返回 null

b) 对于 Phantom 引用:
1. gc 检测到可以从内存中删除对象
2. 当我们调用 clear 或链接到来自队列的引用时对放入 ReferenceQueue 的对象的引用
变得无法访问,并且 gc 看到 3. finalize 方法调用
4.
为弱/软引用释放内存:
1. gc 检测到可以从内存中删除对象
2. 完成方法调用
3. 释放内存
4. 对放入队列的对象的引用

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

2 回答 2

5

也许,以下程序会有所帮助:

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

由于多线程,前两条消息的顺序有时会有所不同。有时,幻像引用在 3 次轮询后被报告入队,这表明它花费了超过指定的 100 毫秒。

关键是

  • 软引用和弱引用在开始完成之前或正确时被清除并入队
  • 幻影引用在finalization之后入队,假设对象没有泄露finalize方法,否则在对象再次变得不可访问后入队
  • (非平凡的)finalize()方法的存在导致需要至少一个额外的垃圾收集周期来检测对象不可访问或再次幻像可访问

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

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

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

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


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

实际应用程序经常使用引用类的子类来向它们添加更多信息。这就是WeakHashMap.Entry扩展WeakReference并记住哈希码和值的情况。清理可以在法线贴图操作中完成,不需要任何线程同步。这对于方法来说是不可能的finalize(),除了 map 实现不能将finalize()方法推送到键的类中。

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

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

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

于 2017-02-02T09:49:45.713 回答
1

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

可以随时做。每当您需要处理引用被破坏时,您都应该这样做。

2) 我能得到哪些帮助?

我不明白这个问题

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

的目的PhantomReference是成为常规最终确定的更灵活的替代方案。但是,为了使其工作,必须将引用排入队列以使完成替换代码工作。(PhantomReference未入队的 A 无法处理。)

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

4) 让 ReferenceQueue 的 get 方法总是返回 null 的原因是什么?

ReferenceQueueAPI没有方法,所以我猜你在get()谈论PhantomReferenceAPI。之所以有get()方法,是为了和超类兼容。get()定义返回的原因null如下:

“为了确保可回收对象保持不变,可能无法检索幻影引用的所指对象:幻影引用的 get 方法始终返回 null。”

(参见javadoc。)

换句话说,这样做是为了使所指对象无法“复活”。

更新

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

幻影引用的区别在于该get()方法总是返回null。(所以javadoc中的解释是......没有说服力。)

于 2016-12-29T11:59:29.927 回答