java虚拟机通过可达性分析算法判断对象是否“死亡”:从gc roots(如虚拟机栈局部变量、静态属性、常量池对象、jni引用等)出发,沿引用链可达的对象存活,不可达的则可回收;该算法规避了引用计数法的循环引用缺陷,且根节点枚举阶段需stw。

一个对象是否“死亡”,Java 虚拟机不看它有没有被显式释放,也不靠程序员手动标记——而是看它还能不能被“触达”。可达性分析算法的核心就一句话:从一组确定不会被回收的根对象(GC Roots)出发,沿着引用链能走到的对象,就是活的;走不到的,就是可回收的“死亡”对象。
哪些对象能当 GC Roots?
GC Roots 不是随便选的,必须是 JVM 在任意时刻都明确知道“绝不能动”的对象。常见类型包括:
- 虚拟机栈中,每个线程栈帧里的本地变量表所引用的对象(比如方法参数、局部变量指向的实例)
- 方法区中,类的静态属性(
static字段)引用的对象 - 方法区中,常量池里引用的对象(如字符串常量、final static 基本类型包装类等)
- 本地方法栈中,JNI(Native 方法)正在使用的 Java 对象
引用链是怎么形成的?
引用链不是单层关系,而是逐级传递的可达路径。例如:
A 引用 B,B 引用 C,C 引用 D —— 那么从 A(若 A 是 GC Root)出发,D 就是可达的,哪怕中间经过多层跳转。只要整条链上每一步都是强引用(未被置为 null、未被覆盖、未被回收),D 就算“活着”。一旦某一级引用断开(比如 B = null),那从 B 往后的整个子图(包括 C、D)就可能变成不可达。
为什么引用计数法没被采用?
虽然引用计数法简单直观(对象被引用一次 +1,失效一次 -1,归零即回收),但它无法解决循环引用问题。比如对象 A 持有 B 的引用,B 也持有 A 的引用,但二者外部再无任何引用指向它们——此时计数都不为 0,却实际已成垃圾。JVM 选择可达性分析,正是因为它天然规避了这种误判。
判定过程会暂停程序吗?
会。枚举 GC Roots 这一步必须保证对象引用关系“静止”,否则搜到一半引用变了,结果就不准。所以 JVM 会触发 STW(Stop-The-World),让所有用户线程停在安全点(SafePoint),完成根节点扫描后再继续运行。即使是 G1 或 ZGC,也仅在根节点枚举阶段短暂 STW,后续并发标记可并行进行。










