场景
Java GC采用的是reachability analysis算法,那么是否可以这么认为,当对象被判定为不可达GC Roots时,就一定会被GC回收掉呢?接下来我们围绕这个问题,进行简单的探究:
首先要明确,在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)!
(以上仅为个人读书笔记,如有不当,欢迎指正!)