Java虚拟机:怎么确定对象已经死了?

2019/04/14

怎么确定对象已经死了?

怎么确定对象已经死了?怎么确定一个对象已经死了?

引用计数算法

给对象中添加一个引用计数器,每当有个地方引用它,计数器值就加1,引用失效,计数器减1,任何时刻计数器为0的对象就不能再应用了。

很难解决对象之间的相互循环引用。

引用计数收集器可以很快的执行,并且交织在程序运行中,对程序需要不被长时间打断的实时环境比较有利,但其很难解决对象之间相互循环引用的问题。如下面的程序和示意图所示,对象objA和objB之间的引用计数永远不可能为 0,那么这两个对象就永远不能被回收。

a1.png

public class ReferenceCountingGC {
  
        public Object instance = null;

        public static void testGC(){

            ReferenceCountingGC objA = new ReferenceCountingGC ();
            ReferenceCountingGC objB = new ReferenceCountingGC ();

            // 对象之间相互循环引用,对象objA和objB之间的引用计数永远不可能为 0
            objB.instance = objA;
            objA.instance = objB;

            objA = null;
            objB = null;

            System.gc();
    }
    

上述代码最后面两句将objA和objB赋值为null,也就是说objA和objB指向的对象已经不可能再被访问,但是由于它们互相引用对方,导致它们的引用计数器都不为 0,那么垃圾收集器就永远不会回收它们。

可达性分析算法

通过一系列“GC roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链(refenecre chain) ,当一个对象到GcRoot 没有任何的引用链,则证明此对象不可用。

Gcroot对象包括:

  • 虚拟机栈(栈帧中的本地变量表) 中引用的对象。
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(native方法)引用的对象。

a2.png

生存还是死亡

即时当一个对象不可达到,也并非非死不可,这时它们处于一个缓行的阶段要真正判处一个对象死亡,至少要经历2次标记。第一次标记为不可达到。当对象没有覆盖finalize() 方法,或者finalize方法已经被虚拟机调用了,虚拟机都被视为没必要执行(没必要执行是不是直接回收?)

如果一个对象被判定有必要执行,则将这个对象放在一个F-Queue的队列中,并由一个线程去执行它。finalize方法是对象逃脱死亡命运的最后机会。当在finalize方法中重新将之间赋值给了某个变量,那么第二次标记就会被移除。如果对象第二次还没有逃脱,那么基本就被回收了。


public class FinalizeEscapeGc {

    private static FinalizeEscapeGc instance = null;

    public void alive() {
        System.out.println("i'm alive");
    }


    /**
     * 该方法只调用一次
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize()");
        FinalizeEscapeGc.instance = this;
    }

    public static void main(String[] args) throws InterruptedException {
        instance = new FinalizeEscapeGc();
        instance = null;
        System.gc();

        Thread.sleep(500);// finalize() method has a low priority to excute
        if (instance != null) {
            instance.alive();
        } else {
            System.out.println("ooops,i'm dead");
        }


        instance = null;
        System.gc();

        Thread.sleep(500);

        if (instance != null) {
            instance.alive();
        } else {
            System.out.println("ooops,i'm dead");
        }
    }
}

输出:

finalize()

i’m alive

ooops,i’m dead

回收方法区

方法区在hotspot虚拟机称为永久代,永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。回收废弃常量与回收堆类似,当一个字符串“abc”,进入了常量池,且没有任何String对象叫“abc”,那么它将会被回收。

判断一个类为无用的类:

  • 该类的所有实例都被回收
  • 加载该类的classloader已经被回收
  • 该类对应java.lang.class对象没有在任何地方引用,无法通过反射访问该类。
本文为原创文章,转载请标明出处。
本文链接:https://www.fangzhipeng.com/javainterview/2019/04/14/gc-root.html
本文出自方志朋的博客


(转载本站文章请注明作者和出处 方志朋-forezp

宝剑锋从磨砺出,梅花香自苦寒来,用心分享,一起成长,做有温度的攻城狮!

Post Directory