GC是否保证已清除的引用按拓扑顺序排入ReferenceQueue?

时间:2010-03-23 01:42:08

标签: java reference garbage-collection guava

假设有两个对象,AB,并且有一个指针A.x --> B,我们会为WeakReference创建A s和B,以及关联的ReferenceQueue

假设AB都无法访问。直觉BA之前不能被视为无法访问。在这种情况下,我们是否能够以某种方式得到保证,相应的引用将在ReferenceQueue中以直观(没有周期时的拓扑)顺序排队?即ref(A)之前的ref(A)。我不知道 - 如果GC将一堆对象标记为无法访问,然后将它们排列为无特定顺序,该怎么办?

我正在审核番石榴的Finalizer.java,看到这个片段:

private void cleanUp(Reference<?> reference) throws ShutDown {
  ...
  if (reference == frqReference) {
    /*
     * The client no longer has a reference to the
     * FinalizableReferenceQueue. We can stop.
     */
    throw new ShutDown();
  }

frqReference是使用过的ReferenceQueue的PhantomReference,因此如果这是GC,则没有Finalizable {Weak,Soft,Phantom}引用可以存活,因为它们引用了队列。因此,在队列本身可以进行GC之前必须进行GC编辑 - 但是,我们仍然可以保证这些引用将按照它们获得的“{1}}的顺序排入”垃圾收集“(如如果他们逐个获得GC)?代码暗示存在某种保证,否则理论上未处理的引用可以保留在队列中。

由于

3 个答案:

答案 0 :(得分:4)

我很确定答案是肯定的。

JVM规范说明了终结器方法:

  

Java虚拟机对finalize方法调用没有强制排序。可以以任何顺序或甚至同时调用终结器。 (JVM spec 2.17.7

由此我推断,无法保证引用按拓扑顺序排队。

答案 1 :(得分:3)

没有订购保证。在Finalizer.java的情况下,可以在处理所有引用之前关闭线程。请参阅FinalizableReferenceQueue的文档:

  • 保留对此对象的强引用,直到所有关联的

  • 指称已经定稿。如果此对象先前是垃圾回收,
  • 支持线程不会在
  • 上调用{@code finalizeReferent()}
  • 其余参考文献。

这是故意行为。例如,当清除对键和/或值的引用时,我们使用FRQ清除映射条目。如果用户不再具有对地图的引用,并且反过来不再具有对FRQ的引用,则处理这些引用没有意义。

答案 2 :(得分:1)

我认为没有这样的保证。 GC本身没有RAM的完整和即时视图(它不能,因为GC在CPU上运行,一次只能查看几个字节)。在你的例子中,假设一个基本的“标记和扫描”GC,很可能A和B将在相同的标记阶段被宣布无法到达,并且没有按特定顺序扫描在一起。维持拓扑秩序可能会很昂贵。

对于Finalizer,它似乎只能通过FinalizableReferenceQueue实例使用,它会执行一些与类加载器相关的魔法。 Finalizer使用自己的工具来检测它在功能上依赖的FinalizableReferenceQueue何时无法访问;这是运行Finalizer的线程知道它应该退出的时刻。根据我的理解,如果应用程序让GC回收FRQ,那么终结器线程将退出,并且不会处理在FRQ引用之后排队的任何引用。这取决于拓扑顺序或缺乏拓扑顺序,但我无法确定这是否是一个问题。我认为只要处理回收的引用对象很重要,应用程序就不应该删除它的FRQ。