JVM—finalize方法

一、Finalize方法的过程

一个对象真正宣告死亡,至少要经历两次标记过程:如果对象在进行可达性分析之后发现没有与GC Roots相连接的引用链,那它将被第一次标记并且进行一次筛选。筛选的条件是此对象是否有必要执行finalize()方法。


当遇到以下两种情况时,虚拟机将视为“没有必要执行”:
1.对象是否覆盖finalize()方法
2.finalize()方法已经被虚拟机调用过


如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个F-Queue的队列中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程执行。

注:这里的“执行”指的是虚拟机有机会触发这个方法,但并不承诺会等待它运行结束。因为一个对象在finalize()方法中执行是非常缓慢的,甚至有可能会发生死循环,将会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。

finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象在finalize()方法中拯救自己(重新与引用链上任何一个对象建立关联),那么在第二次标记时它将被移除“即将回收”的集合,如果对象这时候还没有逃脱,那基本上就真的被回收了~~~(gg

JVM—finalize方法_第1张图片
对象回收过程中触发finalize()方法

二、一次对象的自我拯救代码清单

public class FinalizeEscapeGC {

public static FinalizeEscapeGC SAVE_HOOK = null;

public void isAlive() {
    System.out.println("yes,I am still alive :)");
}

@Override
protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("finalize method executed!");
    FinalizeEscapeGC.SAVE_HOOK = this;
}

public static void main(String[] args) throws Throwable {
    SAVE_HOOK = new FinalizeEscapeGC();

    //对象第一次成功拯救自己
    SAVE_HOOK = null;
    System.gc();
    //因为finalize方法优先级很低,所以暂停0.5s等待
    Thread.sleep(500);
    if (SAVE_HOOK != null) {
        SAVE_HOOK.isAlive();
    } else {
        System.out.println("no,I am dead :(");
    }

    //第二次拯救失败
    SAVE_HOOK = null;
    System.gc();
    //因为finalize方法优先级很低,所以暂停0.5s等待
    Thread.sleep(500);
    if (SAVE_HOOK != null) {
        SAVE_HOOK.isAlive();
    } else {
        System.out.println("no,I am dead :(");
    }
}}

运行结果:

finalize method executed!

yes,I am still alive :)

no,I am dead :(

从代码清单中可知,SAVE_HOOK对象的finalize()方法确实被GC收集器触发过,并且在被收集前成功逃脱了。

另外,在完全一样的两端代码片段里,第二次的执行结果确实逃脱失败了。这是因为任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法就不会被再次执行。

最后,在JVM中并不鼓励使用finalize()对象来拯救对象。因此它的运行代码非常高昂而且不确定性大。finalize()方法能做的工作,使用try-finally或者其他方式都可以做的更好更及时。

你可能感兴趣的