Java垃圾回收通过可达性分析算法判定对象存活,以GC Roots为起点遍历引用链:虚拟机栈局部变量、方法区静态变量与常量、JNI引用、被锁对象及运行中线程均为GC Roots;能连通即存活,否则标记为可回收。

Java 垃圾回收利用根搜索算法(GC Roots Tracing)判定对象是否存活,核心不是“数引用”,而是“找路径”——只要一个对象能从程序运行中明确可靠的起点被顺藤摸瓜找到,它就算活着;找不到,哪怕还有引用计数,也照收不误。
GC Roots 是哪些“活的起点”?
这些对象天然处于活跃状态,不可能被回收,因此被选作搜索起点。JVM 严格枚举它们,确保不漏判:
- 虚拟机栈中局部变量引用的对象:比如方法里 new 出来的对象,只要该方法还没执行完、变量还在作用域内,它就是 GC Root
-
方法区中静态变量引用的对象:如
public static Map<String, Object> cache = new HashMap<>();中的 HashMap 实例 -
方法区中常量池引用的对象:例如字符串字面量
String s = "OK";中的 "OK"(存于字符串常量池) - 本地方法栈中 JNI 引用的对象:Java 调用 native 方法时,C/C++ 代码持有的 Java 对象句柄
- 被 synchronized 锁住的对象:当前正被某个线程加锁,说明正在被并发使用
可达性分析怎么一步步走通?
不是一次性扫描全堆,而是从 GC Roots 出发,逐层遍历引用链,类似“点亮网络”:
- 初始标记所有 GC Roots 为“已访问”
- 对每个已访问对象,检查它的字段(包括数组元素、成员变量),把所引用的其他对象加入待访问队列
- 重复这个过程,直到队列为空
- 最终未被访问到的对象,即与任何 GC Root 都无引用链连通,判定为可回收
这个过程天然规避循环引用问题——objA ←→ objB 彼此引用,但若二者都脱离了 GC Roots 的引用链,就彻底不可达,会被同时回收。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
立即学习“Java免费学习笔记(深入)”;
为什么不用引用计数?
引用计数看似直观,但在 Java 运行环境中会出错且低效:
- 循环引用失效:两个对象互相持有对方引用,计数都不为 0,但实际已无法从代码逻辑访问
- 更新开销大:每次赋值、出作用域都要增减计数,影响性能
- 多线程同步成本高:计数器操作需原子保护,进一步拖慢吞吐
可达性分析虽需暂停应用(STW)做快照,但现代 JVM 通过三色标记、写屏障等机制大幅压缩停顿时间,准确性和工程可行性远超引用计数。
实际回收前还有一道“安全门”
即使对象被判定为不可达,JVM 也不会立刻回收——如果它重写了 finalize() 方法(已废弃但兼容保留),会先放入 F-Queue 队列,由低优先级 Finalizer 线程尝试执行。若执行中重新与 GC Roots 建立引用(比如把自己赋给某个 static 变量),对象就能“复活”。不过这种机制脆弱、不可靠,且严重影响 GC 效率,Java 9 起已标记为 deprecated,Java 18 后彻底移除。生产环境应避免依赖。

















