Object 的finalize() 方法是否与 C++ 的析构函数作用相同?

张贤 2020年03月10日 78次浏览

Object 的finalize() 方法是否与 C++ 的析构函数作用相同?

  • 与 C++ 的析构函数不同,析构函数的调用时机是确定的,而 finalize() 方法的调用时机是不确定的
  • 当垃圾回收器要宣告一个对象死亡时,至少要经过两次标记。如果对象在进行可达性分析后,发现没有与 GC Roots 相连,就会被第一次标记,并且判断是否覆盖 finalize() 方法。如果对象覆盖了 finalize() 方法,并且没有与 GC Roots 相连,就会被放入 F-Qeueu 队列,并由稍后虚拟机建立的低优先级的 finalize 线程去执行触发 finalize 方法
  • 由于优先级比较低,finalize() 执行时可能随时被终止,不承诺等待其运行结束
  • finalize() 的作用是给予对象最后一次重生的机会
public class Finalization {
    private Finalization finalization;

    @Override
    protected void finalize() {
        System.out.println("Finalized");
        finalization = this;
    }

    public static void main(String[] args) {
        Finalization f = new Finalization();
        System.out.println("First print: " + f);
        f = null;
        System.gc();//调用垃圾回收,触发 finalize() 方法
        System.out.println("Second print: " + f);
        System.out.println(finalization);//虽然在 finalize() 中对 finalization 方法进行了重新赋值,但是 finalize() 方法在运行过程中可能会被提前终止,因此这里的 finalization 对象是 null。
    }
}

上述代码的输出是:

First print: gc.Finalization@60e53b93
Second print: null
null
Finalized

最后一句代码System.out.println(finalization);的输出是 null,虽然在 finalize() 中对 finalization 方法进行了重新赋值,但是 finalize() 方法在运行过程中可能会被提前终止,因此这里的 finalization 对象是 null。
如果在System.gc();后让程序睡眠一段时间,则 finalize() 方法可以运行完成,如下:

public class Finalization {
    private static Finalization finalization;

    @Override
    protected void finalize() {
        System.out.println("Finalized");
        finalization = this;
    }

    public static void main(String[] args) {
        Finalization f = new Finalization();
        System.out.println("First print: " + f);
        f = null;
        System.gc();//调用垃圾回收,触发 finalize() 方法
        try {
            //这里睡眠 1 秒,让 GC 和 finalize() 方法有足够时间运行
            Thread.currentThread().sleep(1000);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("Second print: " + f);
        System.out.println(finalization);//这里的 finalization 的地址和 f 的地址是一样的
    }
}

输出是:

First print: gc.Finalization@60e53b93
Finalized
Second print: null
gc.Finalization@60e53b93

重新赋值后,finalization 的地址和 f 的地址是一样的。但是由于 finalize() 方法运行时机的不确定性很大,且开销也很重,因此不推荐使用。