拯救Java对象!

场景

Java GC采用的是reachability analysis算法,那么当对象被判定为不可达,就一定会被回收?

在Java中对象的回收至少要经历两次标记过程:

1.第一次标记:

对象在Reachability analysis中发现没有与GC Roots相连接的引用,对象将被第一次标记,并且会进行一次筛选,

此次筛选条件为:是否有必要执行finalize()方法。

如果对象没有覆盖finalize()方法、或者finalize()方法已经被JVM调用过,JVM视这两种情况为“没有必要执行”;

2.第二次标记:

如果对象在第一次标记和筛选中被JVM判定为有必要执行finalize()方法,那么对象将被放入F-Queue的队列中,稍后

GC将对F-Queue中的对象进行第二次小规模的标记,如果这时候对象没能想办法逃脱,那对象就真的被回收了!

分析

结合上面的标记筛选过程,怎样拯救被判定为不可达的对象,免于被GC回收?先看如下代码:

public class EscapeGC {

    public  static EscapeGC SAVE_HOOK = null;

    public static void isAlive() {
        System.out.println("i am alive!");
    }
    public static void isDead() {
        System.out.println("i am dead!");
    }

    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();

        System.out.println("finalize method has been executed");

        //save 
        EscapeGC.SAVE_HOOK = this;
    }

    public static void main(String[] args) throws Exception {

        SAVE_HOOK = new EscapeGC();

        /*-----------------------------------------第一次测试--------------------------------------*/

        //置为null,让save_hook指向对象在可达性分析中被判定为不可达。
        SAVE_HOOK = null;

        System.gc();

        //finalize()方法执行的优先级非常低,在这里暂停0.5s等待其执行完毕
        Thread.sleep(500);

        if (SAVE_HOOK!=null) {
            EscapeGC.isAlive();
        }else {
            EscapeGC.isDead();
        }

        /*-----------------------------------------第二次测试--------------------------------------*/
        SAVE_HOOK = null;

        System.gc();

        //finalize()方法执行的优先级非常低,在这里暂停0.5s等待其执行完毕
        Thread.sleep(500);

        if (SAVE_HOOK!=null) {
            EscapeGC.isAlive();
        }else {
            EscapeGC.isDead();
        }
    }
}

打印如下:

finalize method has been executed
i am alive!
i am dead!

结合代码和打印分析:

1.因为我们重写了finalize()方法,那么符合“有必要执行finalize()方法”条件,因此打印了“finalize method has been executed”,

2.我们重写finalize不只为了打印一句话,还添加了“EscapeGC.SAVE_HOOK = this;”这句代码,将this赋值给了类变量;

3.然后gc回收,正常来讲,SAVE_HOOK指向的对象已经被回收,应该打印“i am dead!”这句,但实质上是打印“i am alive!”这一句。

截止这里:我们可以判断,拯救对象成功!

但为何下面在此执行同样的代码,打印的是“i am dead!”,对象死了?

1.因为对象的finalize()方法只会被系统执行调用一次(换个角度,在判断有没有必要执行finalize()方法时,因JVM在第一次GC时已经调用过对象的finalize()方法,那么第二次GC时将被判定为没有必要执行finalize()),

那么面对第二次回收,我们重写的finalize()不会再次被重新执行,

也就相当于对象的第二次拯救失败!正好符合后面只打印“i am dead!”这一句话!

总结

拯救被判定为不可达GC Roots的对象的方法为:

在finalize()方法中,把自己(判定为不可达对象)重新与引用链上的任一个对象建立关联即可,如把this赋值给某个类变量或者对象的成员变量,这里的this也就是被判定为不可达GC Roots的实例对象(相当于重新给它个GC Roots)!

(以上仅为个人读书笔记,如有不当,欢迎指正!)

猜你喜欢

转载自blog.csdn.net/LoveHaloK/article/details/81146214