据我了解,GC以一组初始对象(堆栈,静态对象)开始,并递归遍历它构建可到达对象的图形。然后它将这些对象占用的内存标记为已占用,并假定所有其余内存都是空闲的。
但如果这个'免费'内存包含一个finalize
方法的对象? GC必须调用它,但我不知道它怎么能知道不再可以访问的对象。
我认为GC可以跟踪所有可终结的'他们还活着的物体。如果是这样,那么即使它们还活着的话,拥有可终结的物体是否会使垃圾收集变得更加昂贵?
答案 0 :(得分:4)
它为GC提供了一些具有特殊语义的引用,即Weak,Soft和Phantom引用。对于需要最终确定的对象,还有另一种非public
类型的特殊引用。
现在,当垃圾收集器遍历对象图并遇到这样一个特殊的引用对象时,它不会将通过此引用可到达的对象标记为强可访问,但可以使用特殊语义进行访问。因此,如果一个对象只有终结器可达,那么引用将被排队,这样一个(或一个)终结器线程可以轮询队列并执行finalize()
方法(它不是垃圾收集器本身)调用这种方法)。
换句话说,垃圾收集器从不处理完全无法访问的对象。要将特殊语义应用于可达性,引用对象必须是可访问的,因此可以通过到该引用到达引用对象。在终结器可达性的情况下,在创建对象时调用Finalizer.register
并依次创建Finalizer
的实例,FinalReference
的子类和right in its constructor,它调用add()
方法,该方法将引用插入到全局链表中。因此,所有这些FinalReference
实例都可通过该列表访问,直到实际完成。
由于这个FinalReference
将在对象的实例化时正确创建,如果它的类声明了一个非平凡的finalize()
方法,由于有一个终结要求,已经有一些开销,即使该对象尚未收集。
另一个问题是终结器线程处理的对象可以被该线程访问,甚至可能会逃脱,具体取决于finalize()
方法的作用。但是下一次,这个对象变得无法访问,特殊的引用对象不再存在,所以它可以被视为任何其他无法访问的对象。
如果内存非常低并且必须提前执行下一个垃圾收集以最终回收该对象,这只会是性能问题。但这不会发生在参考实现中(又名“HotSpot”或“OpenJDK”)。实际上,在终结器队列中对象处于挂起状态时可能存在OutOfMemoryError
,其处理可以使更多内存可回收。没有保证最终确定的速度足以满足您的目的。这就是为什么你不应该依赖它。
答案 1 :(得分:2)
但如果这个'免费' memory包含一个带有finalize的对象 方法? GC必须打电话给它,但我不知道它怎么能知道 那些不再可以到达的物体。
我们说我们使用CMS垃圾收集器。在第一阶段成功标记所有活动对象后,它将再次扫描内存并删除所有死对象。 GC线程不直接为这些对象调用finalize
方法。
在创建过程中,它们被JVM包装并添加到终结器队列中(参见java.lang.ref.Finalizer.register(Object)
)。此队列在另一个线程(java.lang.ref.Finalizer.FinalizerThread
)中处理,当没有对该对象的引用时,将调用finalize
方法。有关详细信息,请参阅this blog post。
如果是这样,那么可终结对象会使垃圾收集更多 即使他们还活着也很贵?
正如您现在所看到的,大部分时间它都没有。
答案 2 :(得分:0)
当对象关于以收集垃圾时,会调用finalise
方法。这意味着,当GC确定不再引用该对象时,它可以在其上调用finalise
方法。它不必跟踪要最终确定的对象。
答案 3 :(得分:0)
根据javadoc,finalize
当垃圾收集确定没有对该对象的更多引用时,由对象上的垃圾收集器调用。
所以决定是基于参考计数器或类似的东西。
实际上可能根本没有调用此方法。因此,将它用作析构函数可能不是一个好主意。