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

可达性分析算法不靠数引用次数,而是从一组明确“活着”的起点出发,顺藤摸瓜找出所有真正还在用的对象。GC Roots 就是这些起点——它们不是凭空选的,而是 JVM 在当前运行状态下能 100% 确认不能被回收的变量或对象。只要一个对象能通过任意一条引用链连到任一 GC Root,它就“活”着;断开所有链,才进回收队列。
GC Roots 的真实来源有哪些
这些根对象来自 JVM 运行时结构中天然强依赖的位置,不是代码里随便写的引用:
-
虚拟机栈中的局部变量和参数:比如方法里声明的
Object obj = new Object(),只要该方法还没执行完、栈帧还在,obj就是 GC Root -
方法区的静态字段(static 变量):如
public static Map<String, User> cache = new HashMap<>();中的 map 实例,只要类没卸载,它就一直算根 -
字符串常量池里的字面量:像
String s = "hello";中的"hello",被常量池持有,属于 GC Root - 本地方法栈(JNI)引用的对象:Java 调 C/C++ 时,C 层显式持有的 Java 对象必须存活,否则调用会崩溃
- 被 synchronized 锁住的对象:锁对象在同步块执行期间绝不能被回收,否则锁机制失效
- 正在运行的线程对象本身:主线程、工作线程等,只要处于 RUNNABLE 或 BLOCKED 状态,就是 GC Root
为什么这些位置能当 Roots
关键不在“有没有引用”,而在“是否由程序逻辑或 JVM 内部状态强制保证活跃”:
- 栈帧里的变量由执行上下文兜底,方法没退栈,变量就不能丢
- 静态字段绑定类生命周期,类加载后一直存在,直到被主动卸载(极少见)
- JNI 引用由本地代码控制,JVM 必须信任其有效性,否则跨语言调用不可靠
- 锁对象和线程对象直接参与并发控制,回收它们等于破坏 JVM 基础语义
可达性分析的实际效果
它解决了一个关键问题:循环引用不导致内存泄漏。比如 A 持有 B,B 又持有 A,但两者都没被任何 GC Root 连上,整条链就“悬空”,A 和 B 都会被回收。这比引用计数法更贴近程序真实使用意图。
标记过程本质是图遍历——把堆看作一张有向图,对象是节点,引用是边,GC Roots 是入口点。JVM 通常用深度优先(DFS)或三色标记(white/gray/black)实现,支持并发扫描,减少 STW 时间。
开发中容易误判的“假活跃”
有些对象看似没用了,却因意外成为 GC Root 或被 Root 间接牵连:
- 静态集合长期缓存对象,即使业务逻辑已弃用,只要没清空,对象就永远可达
- ThreadLocal 没及时
remove(),线程复用时旧值残留,形成隐式强引用 - 监听器、回调注册后忘记反注册,被框架内部静态容器持有着
- 未关闭的资源(如数据库连接、文件流)可能被 Finalizer 队列临时引用,延迟回收

















