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

可达性分析算法是现代垃圾回收器判断对象是否存活的核心方法,它不直接找“垃圾”,而是从一组确定活跃的起点出发,标记所有能被访问到的对象——没被标记的,才是可回收的。
核心思路:从根出发,顺藤摸瓜
算法把一些明确不能被回收的对象作为“GC Roots”,比如正在执行的方法里的局部变量、静态字段、常量池中的引用、JNI调用中持有的对象等。从这些根开始,沿着所有引用关系向下遍历,走过的路径叫“引用链”。只要一个对象能通过某条引用链连到任一GC Root,它就被视为“可达”,也就是存活。
- 不是靠计数,所以天然避开循环引用问题(比如A引用B、B又引用A,但两者都没被任何Root连上,照样会被回收)
- 标记的是“活对象”,不是“死对象”,效率更高、逻辑更清晰
- 实际实现中通常配合“三色标记”等机制,支持并发或增量式扫描,减少停顿
GC Roots有哪些典型来源
这些对象之所以能当Root,是因为它们在当前时刻一定被程序逻辑强依赖,不可能突然失效:
- 虚拟机栈中每个线程的栈帧里,正在使用的局部变量和参数
- 本地方法栈中JNI(Java Native Interface)所引用的Java对象
- 方法区中类的静态属性(static字段)所引用的对象
- 方法区中常量池里字符串字面量、类引用等对应的对象
- 被synchronized锁住的对象(表明正被线程争用)
- JVM内部关键对象,如系统类加载器、某些异常类、JMX Bean等
为什么它比引用计数更实用
引用计数法虽然直观,但有两个硬伤:一是每次读写引用都要更新计数,开销大;二是无法处理循环引用,容易内存泄漏。可达性分析没有这些限制:
- 不需要为每个对象维护额外计数字段,节省内存
- 一次遍历就能覆盖整个对象图,结果准确
- 天然适配分代收集——比如做新生代GC时,会把老年代中可能引用新生代对象的“跨代引用”也临时加入Root集合,保证精度
注意:不可达 ≠ 立刻回收
一个对象第一次被发现不可达,不会马上进回收队列。JVM通常会再做一次筛选(比如判断是否重写了finalize方法、是否还有弱/软引用等),确保它真的没机会复活。只有经过两次标记且确认无引用后,才会真正进入回收流程。









