GC roots 如何判断对象不可达

查找内存中不再使用的对象

引用计数法

引用计数法就是如果一个对象没有被任何引用指向,则可视之为垃圾。这种方法的缺点就是不能检测到环的存在。

public class MyObject {
    public Object ref = null;
    public static void main(String[] args) {
        MyObject myObject1 = new MyObject();
        MyObject myObject2 = new MyObject();
        myObject1.ref = myObject2;
        myObject2.ref = myObject1;
        myObject1 = null;
        myObject2 = null;
    }
}


如果采用的是引用计数算法:
再回到前面代码 GcDemo 的 main 方法共分为 6 个步骤:
Step1:GcObject 实例 1 的引用计数加 1,实例 1 的引用计数 =1;
Step2:GcObject 实例 2 的引用计数加 1,实例 2 的引用计数 =1;
Step3:GcObject 实例 2 的引用计数再加 1,实例 2 的引用计数 =2;
Step4:GcObject 实例 1 的引用计数再加 1,实例 1 的引用计数 =2;
执行到 Step 4,则 GcObject 实例 1 和实例 2 的引用计数都等于 2。
接下来继续结果图:

Step5:栈帧中 obj1 不再指向 Java 堆,GcObject 实例 1 的引用计数减 1,结果为 1;
Step6:栈帧中 obj2 不再指向 Java 堆,GcObject 实例 2 的引用计数减 1,结果为 1。
到此,发现 GcObject 实例 1 和实例 2 的计数引用都不为 0,那么如果采用的引用计数算法的话,那么这两个实例所占的内存将得不到释放,这便产生了内存泄露。

根搜索算法

这是目前主流的虚拟机都是采用 GC Roots Tracing 算法,比如 Sun 的 Hotspot 虚拟机便是采用该算法。 该算法的核心算法是从GC Roots 对象作为起始点,利用数学中图论知识,图中可达对象便是存活对象,而不可达对象则是需要回收的垃圾内存。这里涉及两个概念,一是 GC Roots,一是可达性。

GCroots 的节点:全局性的引用(常量或静态属性)与执行上下文(例如栈帧中的局部变量表中)
那么可以作为 GC Roots 的对象(见下图):

  • 虚拟机栈的栈帧的局部变量表所引用的对象;
  • 本地方法栈的 JNI 所引用的对象;
  • 方法区的静态变量和常量所引用的对象;

关于可达性的对象,便是能与 GC Roots 构成连通图的对象,如下图:

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

从上图,reference1、reference2、reference3 都是 GC Roots,可以看出:
reference1-> 对象实例 1;
reference2-> 对象实例 2;
reference3-> 对象实例 4;
reference3-> 对象实例 4 -> 对象实例 6;
可以得出对象实例 1、2、4、6 都具有 GC Roots 可达性,也就是存活对象,不能被 GC 回收的对象。
而对于对象实例 3、5 直接虽然连通,但并没有任何一个 GC Roots 与之相连,这便是 GC Roots 不可达的对象,这就是 GC 需要回收的垃圾对象。
转载:https://blog.csdn.net/sinat_33087001/article/details/77987463