假设有两个对象,A
和B
,并且有一个指针A.x --> B
,我们会为WeakReference
创建A
s和B
,以及关联的ReferenceQueue
。
假设A
和B
都无法访问。直觉B
在A
之前不能被视为无法访问。在这种情况下,我们是否能够以某种方式得到保证,相应的引用将在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)?代码暗示存在某种保证,否则理论上未处理的引用可以保留在队列中。
由于
答案 0 :(得分:4)
我很确定答案是肯定的。
JVM规范说明了终结器方法:
Java虚拟机对finalize方法调用没有强制排序。可以以任何顺序或甚至同时调用终结器。 (JVM spec 2.17.7)
由此我推断,无法保证引用按拓扑顺序排队。
答案 1 :(得分:3)
没有订购保证。在Finalizer.java的情况下,可以在处理所有引用之前关闭线程。请参阅FinalizableReferenceQueue的文档:
保留对此对象的强引用,直到所有关联的
这是故意行为。例如,当清除对键和/或值的引用时,我们使用FRQ清除映射条目。如果用户不再具有对地图的引用,并且反过来不再具有对FRQ的引用,则处理这些引用没有意义。
答案 2 :(得分:1)
我认为没有这样的保证。 GC本身没有RAM的完整和即时视图(它不能,因为GC在CPU上运行,一次只能查看几个字节)。在你的例子中,假设一个基本的“标记和扫描”GC,很可能A和B将在相同的标记阶段被宣布无法到达,并且没有按特定顺序扫描在一起。维持拓扑秩序可能会很昂贵。
对于Finalizer
,它似乎只能通过FinalizableReferenceQueue
实例使用,它会执行一些与类加载器相关的魔法。 Finalizer
使用自己的工具来检测它在功能上依赖的FinalizableReferenceQueue
何时无法访问;这是运行Finalizer
的线程知道它应该退出的时刻。根据我的理解,如果应用程序让GC回收FRQ,那么终结器线程将退出,并且不会处理在FRQ引用之后排队的任何引用。这取决于拓扑顺序或缺乏拓扑顺序,但我无法确定这是否是一个问题。我认为只要处理回收的引用对象很重要,应用程序就不应该删除它的FRQ。