乐趣区

关于jvm:Java虚拟机怎么确定对象已经死了

怎么确定对象曾经死了?

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

援用计数算法

给对象中增加一个援用计数器,每当有个中央援用它,计数器值就加 1,援用生效,计数器减 1,任何时刻计数器为 0 的对象就不能再利用了。

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

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

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 办法)援用的对象。

生存还是死亡

即时当一个对象不可达到,也并非非死不可,这时它们处于一个疾驶的阶段要真正判处一个对象死亡,至多要经验 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 对象没有在任何中央援用,无奈通过反射拜访该类。
退出移动版