JVM判断对象是否存活采用可达性分析算法而非引用计数法,因后者无法解决循环引用且有额外开销;前者以GC Roots为起点遍历引用链,断开即判定为可回收。

JVM 判断对象是否存活,核心是看程序还能不能“触达”这个对象。目前所有主流 JVM(如 HotSpot)都采用可达性分析算法,而不是引用计数法——后者虽简单高效,但无法处理循环引用,已被 Java 明确弃用。
可达性分析:从 GC Roots 出发找“活路”
该算法把一组特定对象当作起点(即 GC Roots),顺着所有引用链往下扫描。只要某个对象能被某条路径连到任意一个 GC Root,它就被认为“还活着”;反之,哪怕对象之间互相引用,只要跟 GC Roots 断开,就判定为可回收。
- 常见 GC Roots 包括:虚拟机栈中正在使用的局部变量、方法区的静态变量、字符串常量池中的引用、本地方法栈里的 JNI 引用、被 synchronized 锁住的对象、活跃线程本身等。
- 关键前提:整个分析过程必须在“一致性快照”下进行,也就是要暂停用户线程(Stop-The-World),否则引用关系边扫边变,结果不可靠。
- 注意点:标记只是第一步,被标记为不可达的对象不会立刻释放内存,后续还要经过筛选(比如是否重写 finalize)、二次标记等阶段才真正进入回收队列。
为什么不用引用计数法?
引用计数法给每个对象维护一个计数器,引用增加时 +1,失效时 -1,归零即回收。它实现轻量、响应快,Python 和 FlashPlayer 确实用它,但在 Java 中行不通:
- 两个对象互相持有对方引用(objA.instance = objB; objB.instance = objA),即使外部已无任何引用,它们的计数器仍为 1,永远无法归零。
- 计数器本身占用额外内存,且每次赋值都要原子更新,带来并发开销和复杂性。
- JVM 的准确式 GC 设计更适配图遍历类算法,而非逐个维护计数。
实际开发中要注意什么?
理解存活判定机制,能帮你避开一些典型内存陷阱:
- 静态集合类(如 static Map)若长期持有对象引用,会让本该回收的对象一直“挂”在 GC Roots 上。
- 未关闭的资源(如 InputStream、数据库连接)可能通过 ThreadLocal 或内部监听器间接延长对象生命周期。
- 使用弱引用(WeakReference)或软引用(SoftReference)可让对象在 GC 时更易被回收,适合做缓存等场景。
验证对象是否真被回收的小技巧
可以通过 JVM 参数配合代码观察效果:
- 加参数 -XX:+PrintGCDetails -verbose:gc 启动程序,触发 System.gc() 后查看日志中是否有对应对象所在区域的回收记录。
- 重写对象的 finalize() 方法(不推荐用于生产)并打印日志,可粗略判断是否被标记为待回收(注意:finalize 只执行一次,且 JDK 9+ 已标记为废弃)。
- 用 JVisualVM 或 MAT 分析堆转储(heap dump),直接查看对象的引用链,确认它是否真的与 GC Roots 断开。

















