1

我正在学习幻像引用,我很困惑当引用对象被垃圾收集时,幻像引用是如何排队的。

这是我的代码

    Object s = new Object();

    ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
    PhantomReference<Object> ref = new PhantomReference<Object>(s, queue);

    s = null;
    System.gc();
    TimeUnit.SECONDS.sleep(1);
    System.out.println(queue.poll());

正如预期的那样,queue.poll 将返回幻像引用:ref。

但是如果我对代码做一点改动:删除局部变量“ref”,queue.poll 将返回 null。

所以我可以推断,当 JVM 尝试对对象进行垃圾收集时,它会检查所有引用以查看是否有任何可以到达该对象的引用。

这应该是一个非常缓慢的进展。?

我设计了一个程序:使用幻像引用来跟踪资源泄漏。分配资源时,新建了一个幻像引用并绑定到资源。然后我发现:必须存储所有幻像引用,否则幻像引用不会入队。

我检查了netty代码,发现netty存储了所有幻像引用:io.netty.util.ResourceLeakDetector#allLeaks。

4

2 回答 2

1

垃圾收集器将引用对象视为任何其他对象。从文档中

如果已注册的引用本身变得无法访问,那么它将永远不会入队。

理论上,是的,使用引用对象会导致垃圾收集效率低下,所以我不会为每个应用程序对象创建一个引用对象。但在实践中,引用对象的集合往往非常小:它们旨在跟踪数据库连接等资源,而不是任意对象。

于 2018-02-25T11:43:34.240 回答
1

正如规范所述,“<em>如果一个已注册的引用本身变得不可访问,那么它将永远不会入队”。然而,这并不奇怪,因为没有定义特殊可达性状态的可达性引用对象,就没有特殊的可达性状态。规范明确说明的是,只要引用对象尚未入队,就没有从队列到引用对象的引用。

所以我可以推断,当 JVM 尝试对对象进行垃圾收集时,它会检查所有引用以查看是否有任何可以到达该对象的引用。

除了 JVM 从不尝试收集单个对象之外,您刚刚描述了垃圾收集的全部内容,遍历所有引用以找出仍然可以访问的对象。

这应该是一个非常缓慢的进展。?

如果垃圾收集器对每个对象再次执行此操作,将会非常慢。但是垃圾收集器会遍历所有对象。遍历过程中未遇到的对象本身就是垃圾,因此这些无法访问的对象不需要任何特殊处理,其中包括废弃的引用对象。

尽管如此,垃圾收集当然会产生开销,这就是为什么System.gc()不应该被调用的原因,除非像在您的示例程序中那样进行调试。

于 2018-03-26T12:36:54.050 回答