可达性算法中不可达的对象是否一定会死亡(不一定)

我们应该都知道Java虚拟机在进行垃圾回收操作的时候,会先进行垃圾判定,会使用引用计数法可达性算法来进行对象是否回收判断;

可达性算法的基本思路是通过”GC Roots“的对象作为起始点,从这些点开始往下搜索,搜索所走过的路径为引用链,当一个对象到“GC Roots”没有任何引用链相连,证明该对象是不可达的,即不可用,是可回收对象;

在Java中可以做GC Roots的对象包含以下几种:

1、虚拟机栈(栈帧中的本地变量表)中引用的对象;
2、方法区中的静态变量所引用的对象;
3、方法去中常量引用的对象;
4、本地方法栈中JNI(即Native方法)引用的对象;

但是可达性算法不可达的对象就一定会死亡,会被回收吗?

答案是否定的!

即使在可达性算法中不可达的对象也不一定是非死不可的,这时候它们暂时处于“缓刑”阶段,要真正宣告它的死亡还需要经历两次的标记阶段。

第一次标记:
在对象可达性算法不可达时,进行第一次标记,并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize方法或者该方法被虚拟机调用过,虚拟机将这两种情况视为“没有必要去执行”。

如果该对象被判定为有必要执行finalize()方法,那么这个对象会被放置到一个叫做F-Queue的队列中,并在稍后由一个虚拟机自动建立的、低优先级的Finalize线程去执行它。这里所谓的执行就是去触发该方法,但是并不会承诺等待它执行结束,这样做的原因是,如果对象在finalize()方法中执行缓慢,或者发生死循环,将会导致整个队列中的对象处于等待之中。

第二次标记:
finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中拯救自己——只要重新与引用链上的一个对象重新建立关联即可,譬如将自己(this关键字)赋值给某个类变量或者成员变量,那么在第二次标记的时候就会被移除“即将回收”的集合;如果对象这时候还没有逃脱,那么就会被真的回收了!

示例代码:

public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive(){
        System.out.println("yes, i am still alive!");
    }
    @Override
    public void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed!");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }
    public static void main(String[] args) throws InterruptedException {
        SAVE_HOOK = new FinalizeEscapeGC();

        //对象第一次拯救自己
        SAVE_HOOK = null;
        System.gc();
        //因为finalize方法优先级很低所以暂停0.5秒等待
        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.5秒等待
        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!

注意:第二次自救失败是因为任何一个对象的finalize()方法只能执行一次,如果第二次回收,就不会执行finalize方法了!

发布了49 篇原创文章 · 获赞 11 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_41999455/article/details/102608447