可达性分析算法以gc roots为起点遍历引用链判定对象存活,gc roots包括虚拟机栈中局部变量、方法区静态字段与常量、本地方法栈jni引用、被锁对象及运行中线程。

Java 虚拟机判断一个对象是否该被回收,不看它“有没有被引用”,而是看它“能不能从 GC Roots 被访问到”。这就是根搜索算法(也叫可达性分析算法)的核心逻辑——它不是数引用个数,而是走引用链找路径。
GC Roots 是哪些对象?
GC Roots 是一组特殊起点,JVM 从它们出发扫描整个对象图。常见的 Roots 包括:
- Java 线程栈帧中的局部变量所引用的对象(比如 main 方法里 new 出来的对象)
- 方法区中静态字段引用的对象(如 public static final 常量、类的 static 成员)
- 方法区中常量池引用的对象(如字符串字面量 "hello")
- 本地方法栈中 JNI 引用的对象(Java 调用 C/C++ 时保留的引用)
为什么不用引用计数法?
引用计数法虽然实现简单、判定快,但存在根本缺陷:
- 无法处理循环引用:A 持有 B,B 持有 A,外部已无引用,但两者计数都不为 0,导致内存泄漏
- 每次赋值都要原子更新计数器,带来额外开销
- 每个对象需额外空间存计数器,增大内存负担
主流 JVM(如 HotSpot)自 JDK 1.2 起就彻底弃用该算法,仅作教学对比。
可达性分析怎么执行?
一次典型的 GC 前标记阶段会这样进行:
- 暂停所有用户线程(Stop-The-World),确保对象图稳定
- 以所有 GC Roots 为起点,并发或串行遍历所有可达引用链
- 将沿途访问到的对象标记为“存活”;未被标记的即为“不可达”
- 不可达对象不会立刻回收,JVM 可能对其执行 finalize()(已废弃,不推荐依赖)做最后挽救
注意:对象之间互相引用,只要整条链脱离 GC Roots,就属于垃圾——哪怕它们形成闭环。
不同代使用不同回收策略
可达性分析是判断“谁该死”的统一标准,但“怎么清理”取决于所在内存区域:
- 新生代(Eden + Survivor):采用复制算法,基于可达性分析结果,只复制存活对象到另一块 Survivor 区
- 老年代:常用标记-清除或标记-整理,同样先靠可达性分析识别存活对象,再决定清空还是移动
- 元空间(替代永久代):不参与对象可达性分析,只清理无用类信息(如类加载器已卸载且类无实例)
无论哪一代,GC Roots 的集合和引用链的遍历逻辑保持一致,这是整个 GC 机制可靠性的基础。











