可达性分析算法是JVM判断对象存活的核心方法,从GC Roots(如虚拟机栈局部变量、静态字段、常量池引用、JNI引用等)出发沿引用链标记可达对象,未被标记的即为可回收垃圾,天然解决循环引用问题。

Java 虚拟机不靠“谁还用着它”来猜对象是否该活,而是从一组明确、绝对不能回收的起点出发,顺引用链往下找——这些起点就是 GC Roots。只要能从任一 GC Root 出发,沿着强引用链走到某个对象,它就被认定为存活;断开所有路径,才进入回收候选队列。
哪些对象能当 GC Roots?
GC Roots 不是随便挑的,它们必须满足一个硬条件:在当前 JVM 运行状态下,100% 确认不能被回收。常见类型包括:
- 虚拟机栈中局部变量表里的引用:比如方法里刚 new 出来的对象、传入的参数、临时变量指向的对象
-
方法区中静态字段引用的对象:public static List
cache = new ArrayList(); 这个 list 实例就是 GC Root 的延伸目标 - 方法区常量池中的引用:如字符串字面量 "hello" 对应的 String 对象(在 JDK 7+ 存于堆中,但仍由常量池强引用)
- 本地方法栈(JNI)中 Native 方法持有的引用:比如通过 JNI 调用 C 代码时传过去的 Java 对象句柄
- 正在运行的 Java 线程对象本身、被 synchronized 锁住的对象、系统类加载器加载的关键 Class 对象等
为什么只看“可达性”,不数引用次数?
引用计数法看似直观,但存在致命缺陷:两个对象互相持有强引用(A.obj = B; B.obj = A),即使外部再无任何引用,它们的计数器也永远不为 0。JVM 无法识别这种“逻辑死亡”。可达性分析绕开了这个问题——它不关心对象之间怎么连,只关心能不能从根出发走到它。哪怕 A 和 B 形成闭环,只要没人从 GC Root 指向 A 或 B,整条环就全不可达,一起回收。
引用类型会影响“是否算可达”吗?
会,但只影响“最后一次机会”:
- 强引用:只要它存在,对象一定算“可达”,不会被回收
- 软引用:内存不足时 GC 可选择性断开它,断开后若再无强引用路径,对象即不可达
- 弱引用:GC 时直接清空,清空后若无其他强引用路径,对象立刻不可达
- 虚引用:不影响可达性判断,仅用于对象被回收前的回调通知
注意:只有强引用构成的路径,才算有效“引用链”。软/弱/虚引用本身不能作为 GC Roots,也不能让一个对象因此变成“可达”。
对象真的不可达,就立刻回收了吗?
不一定。JVM 给了两次标记机会:
- 第一次标记:发现不可达 → 判定为“可能回收”,再检查是否重写了 finalize() 方法且未执行过
- 如果需要执行 finalize(),对象会被放入 F-Queue 队列,由低优先级 Finalizer 线程触发该方法(不保证执行完成)
- 第二次标记:finalize() 执行完毕后,若对象仍未被任何强引用重新关联,才真正进入回收队列
不过 finalize() 已被标记为 deprecated,实际开发中不应依赖它做资源清理,而应使用 try-with-resources 或 Cleaner 替代。










