如果垃圾收集器在同一代码中两次保留内存,是否可以调用 finalize() 方法两次?

Roh*_*kar 3 java garbage-collection

我在很多地方发现,当垃圾收集器或 System.gc() 成功保留冗余对象消耗的内存而不再引用它时,会调用 java 中的 finalize() 方法。还发现这个方法调用不超过一次。我对 Java 并不陌生,但也没有多少经验。我可能对它有错误的理解,但让我们说一段代码

public class Solution {
    @Override
    protected void finalize(){
        System.out.print("method called");
    }
    public static void main(String... args){
        Solution obj1= new Solution();
        Solution obj2 = new Solution();
        Solution obj3 = new Solution();
        System.gc();
        obj1=obj2;
        System.gc();
        obj3=null;
        System.gc();
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,finalize 方法被调用了两次,因为内存堆两次都符合垃圾清理的条件。所以,我有点困惑,我是否知道整件事是正确的,或者它是否应该按照它的行为方式行事。

Ste*_*n C 6

finalize()不会。该方法只会被 GC 在一个对象上调用一次。JVM 在对象头(我认为)中设置了一个标志,表示它已经完成,并且不会再次完成它。

javadoc中明确指出这一点:

“对于任何给定的对象,Java 虚拟机永远不会多次调用 finalize 方法。”

当然,没有什么可以阻止对象方法this.finalize()多次调用。


请注意,finalize()弃用Java中9,后来在Javadoc中说明理由。建议您改用以下方法之一:

  • AutoCloseable + 尝试使用资源
  • Cleaner
  • PhantomReference

有人是这样评论的:

finalize()被收集的每一个都 被调用Object

出于几个原因,情况并非如此。

  • javadoc明确声明不保证finalize会被调用。可以保证的是,它会在对象的存储被回收之前被调用(一次)。这是一个比评论所做的陈述更弱的陈述。

    垃圾收集对象可能无法最终确定的一种情况是 JVM 在 GC 运行后不久退出。

    当类finalize方法从不返回1时,会发生另一种(病态的)情况。当该类的实例完成时,终结器线程将卡住。当所有终结器线程都以这种方式卡住时,就无法终结更多可终结对象2

  • 如果Object::finalize未在类中覆盖,JVM 将跳过该类的终结步骤。


1 - 这可能是由于无限循环,或者因为该finalize()方法卡在等待锁定或等待从未发生的内部或外部“事件”。还要注意,在这种情况下,“从不”可能意味着“很长时间没有”。整体影响可能相同。
2 - 对象将无限期地位于终结队列中。这是内存泄漏。