一篇文章带你深入了解 Java 虚拟机中的 GC Roots 及垃圾回收如何确定垃圾

一、什么是 GC Roots

Java 堆是垃圾收集器管理的内存区域,因此一些资料中它也被称作“GC堆”(Garbage Collected Heap)

GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区、栈和本地方法区不被 GC 所管理,因而选择这些区域内的对象作为 GC roots ,被 GC roots 引用的对象不被 GC 回收

“GC roots”,或者说 tracing GC 的“根集合”,就是一组必须活跃的引用,不是对象,GC会收集那些不是GC roots 且没有被GC roots引用的对象==

Tracing GC的根本思路就是:给定一个集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可到达的)对象就被判定为存活,其余对象(也就是没有被遍历到的)就自然被判定为死亡。

tracing GC的本质是通过找出所有活对象来把其余空间认定为“无用”,而不是找出所有死掉的对象并回收它们占用的空间

GC roots这组引用是tracing GC的起点。要实现语义正确的tracing GC,就必须要能完整枚举出所有的GC roots,否则就可能会漏扫描应该存活的对象,导致GC错误回收了这些被漏扫的活对象。

这就像任何递归定义的关系一样,如果只定义了递推项而不定义初始项的话,关系就无法成立,无从开始;而如果初始项定义漏了内容的话,递推出去也会漏内容。

二、JVM垃圾回收时如何确定垃圾?

简单来说就是内存中已经不再被使用的空间就是垃圾

1. 引用计数法

Java中,引用和对象是有关联的。如果要操作对象则必须用引用进行。

因此,很显然一个简单的办法就是通过引用计数来判断一个对象是否可以回收。简单说,给对象中添加一个引用计数器
每当有一个地方引用它,计数器值加1
每当有一个引用失效,计数器值减1
任何时刻计数器值为零的对象就是不可能再被使用的,那么这个对象就是可回收对象。

那么为什么主流的Java虚拟机里面都没有选用这个方法呢?其中最主要的原因是它很难解决对象之间相互循环引用的问题。

该算法存在但目前无人用了,解决不了循环引用的问题,了解即可。

一篇文章带你深入了解 Java 虚拟机中的 GC Roots 及垃圾回收如何确定垃圾

2. 枚举根节点做可达性分析

搜索路径算法
为了解决引用计数法的循环引用个问题,Java使用了可达性分析的方法

一篇文章带你深入了解 Java 虚拟机中的 GC Roots 及垃圾回收如何确定垃圾
所谓 GC Roots 或者说 Tracing Roots的“根集合” 就是一组必须活跃的引用

基本思路就是通过一系列名为 GC Roots 的对象作为起始点,从这个被称为GC Roots的对象开始向下搜索,如果一个对象到GC Roots没有任何引用链相连,则说明此对象不可用。
也即给定一个集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可到达的)对象就被判定为存活,没有被遍历到的对象就被判定为死亡

一篇文章带你深入了解 Java 虚拟机中的 GC Roots 及垃圾回收如何确定垃圾
蓝色部分是从GC Roots出发,能够循环可达,而白色部分,从GC Roots出发,无法到达

一句话理解GC Roots:假设我们现在有三个实体,分别是 人,狗,毛衣
然后他们之间的关系是:人 牵着 狗,狗穿着毛衣,他们之间是强连接的关系,有一天人消失了,只剩下狗狗 和 毛衣,这个时候,把人想象成 GC Roots,因为 人 和 狗之间失去了绳子连接,那么狗可能被回收,也就是被警察抓起来,被送到流浪狗寄养所
假设狗和人有强连接的时候,狗狗就不会被当成是流浪狗

那些对象可以当做GC Roots

(1)虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中的引用对象
(2)方法区中的类静态属性引用的对象
(3)方法区中常量引用的对象
(4)本地方法栈中的JNI(Native方法)的引用对象

参考

【1】https://www.zhihu.com/question/53613423/answer/135743258
【2】https://www.cnblogs.com/bbgs-xc/p/12839532.html
【3】https://www.zhihu.com/question/50381439