4

我的问题总结了这一切:

  • 强可达 Java PhantomReference 能否阻止垃圾收集器 (GC) 回收其引用对象的内存?

详情如下:

Callum 也发布了这个问题,但没有直接回答。那里的一个回复是指Ethan Nicholas的一篇文章,该文章似乎用“否”回答了我的问题,但我不确定这是正确的。

根据我对 Java API 的阅读,我必须用“是”回答我的问题:

  • 只要不调用 PhantomReference.clear(),并且 PhantomReference 实例本身仍然被强引用,则永远不会回收引用对象的内存,并且引用对象将保持在幻像可达状态。

为了支持这种理解,我将引用Java Docs

  • “与软引用和弱引用不同,幻像引用在入队时不会被垃圾收集器自动清除。通过幻像引用可访问的对象将保持不变,直到所有此类引用都被清除或它们本身变得不可访问。”

例如,假设我创建了一个幻像引用并将该实例保存在 PhantomReference 列表中。然后它的所指对象从强可达下降到幻象可达。

如果您查看 com.google.common.base.internal.Finalizer.java,您将看到以下代码:

  private void cleanUp(参考参考)抛出 ShutDown {
      ...

      /*
       * 这是为了虚拟引用的好处。软弱无力
       * 此时引用已被清除。
       */
      参考.clear();

      ...
  }

我更喜欢有经验的人来回应,而不是进行网络搜索并为我提供链接。谢谢!

4

1 回答 1

2

你把两件事混为一谈了。链接的问题不是关于所指对象,而是关于PhantomReference实例。与PhantomReference所有引用对象一样,实例可以像任何其他对象一样被垃圾收集,只要它没有被排队。这是在包规范中指定的:

注册的引用对象与其队列之间的关系是片面的。也就是说,队列不会跟踪向其注册的引用。如果已注册的引用本身变得无法访问,那么它将永远不会入队。只要程序对其所指对象感兴趣,程序就有责任使用引用对象来确保对象保持可访问性。

但你的问题是关于所指的。此外,引用的代码是关于处理已经入队甚至从队列中检索的引用。

在这个地方,您引用的文档适用。在 Java 8 之前(包括 Java 8),reachablePhantomReference的引用对象不会自动清除,因此引用对象保持幻觉可达,直到引用被清除或本身变得不可访问。因此,引用的代码在明确清除引用以允许早期回收方面是正确的,但是,差异仅影响清理方法执行的持续时间,因为之后,PhantomReference它本身可能变得无法访问。


但这不是故事的结局。没有明确的理由为什么指示物应该保持幻象可达而不是被回收。毕竟,清理方法无论如何都无法访问所指对象。

因此 Java 9 放弃了该规则并像任何其他引用一样自动清除幻像引用。所以从 Java 9 开始,手动清除已经入队的幻像引用是不必要的,但当然也没有什么坏处,所以旧软件仍然可以顺利运行。


关于你的例子:

…假设我创建了一个幻像引用并将该实例保存在 PhantomReference 列表中。然后它的所指对象从强可达下降到幻象可达。

仅作为参考的PhantomReference参考不足以成为幻影可达。它还要求没有强引用并且对象已经完成,尽管大多数对象实际上都跳过了完成,因为它们没有自定义finalize()方法。当幻像引用之外还有软引用时,可能取决于配置和内存需求,所指对象是否会成为幻像可达。

于 2017-12-21T15:39:32.727 回答