JVM学习笔记——如何判断对象已死(GC标记)?

有两种方法,分别为:引用计数法可达性分析法

引用计数算法

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

客观的说此方式高效简单,在大部分情况下是一个不错的算法。但不能解决对象之间循环引用的问题。

借助《深入理解Java虚拟机》书中的例子:

    public class TestGC {
     

        public Object instance = null;

        private static final int _1MB = 1024 * 1024;

        private byte[] bigSize = new byte[2 * _1MB];

        public static void main(String[] args) {
     
            TestGC objA = new TestGC();
            TestGC objB = new TestGC();

            objA.instance = objB;
            objB.instance = objA;

            objA = null;
            objB = null;

            System.gc();
        }
    }

对象objA和objB都有instance,赋值objA.instance = objBobjB.instance = objA,实际上这两个对象已经不可能再被访问,但是因为他们互相引用者对方,导致它们的引用计数都为0,所以引用计数法无法通知GC回收它们。可以得出结论:Java虚拟机不是通过引用计数法来判断对象是否存活的

可达性分析算法

在主流的语言程序中,都是通过可达性分析来判断对象是否存活的

基本思想就是通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连,则此对象是不可用的。

对象object5,object6,object7虽然互相有关联,但是它们的GC Roots是不可达的,所以基本被判断为是可回收的对象。
JVM学习笔记——如何判断对象已死(GC标记)?_第1张图片
如果对象在进行可达性分析后发现没有与GC Roots相连的引用链,也不会立即死亡。它会暂时被标记上并且进行一次筛选,筛选的条件是是否有必要执行finalize()方法。如果被判定有必要执行finaliza()方法,就会进入F-Queue队列中,并有一个虚拟机自动建立的、低优先级的线程去执行它。稍后GC将对F-Queue中的对象进行第二次小规模标记。如果这时还是没有新的关联出现,那基本上就真的被回收了。

Java中可作为GC Roots的对象包括

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

为什么它们可以作为GC Roots?因为这些对象肯定不会被回收。比如,虚拟机栈中是正在执行的方法,所以里面引用的对象不会被回收。

参考书籍

《深入理解Java虚拟机》

你可能感兴趣的:(JVM)