JVM判断对象“已死”的核心是可达性分析算法,即从GC Roots(如虚拟机栈局部变量、静态字段、常量池引用、JNI引用等)出发沿引用链搜索,不可达的对象被标记为可回收;引用计数法因无法解决循环引用问题而未被采用。

JVM 判断对象是否“已死”,核心是确认该对象是否还被程序逻辑所引用、是否还能被访问到。它不依赖对象自己“说”自己没用了,而是通过一套客观、可追踪的引用关系来判定。目前所有主流 JVM(如 HotSpot)都只使用可达性分析算法,引用计数法仅作理论对比,实际未被采用。
可达性分析:从 GC Roots 出发找活路
JVM 会选定一组必须存活的“根对象”(GC Roots),然后顺着所有引用链向下扫描。只要某个对象能通过任意一条引用路径连到 GC Roots,它就被认为“还活着”;反之,如果从任何 GC Root 都无法触达该对象,它就被标记为“已死”,后续可被回收。
- 常见的 GC Roots 包括:虚拟机栈中正在使用的局部变量所引用的对象
- 方法区中类的静态字段(static 变量)引用的对象
- 方法区中常量池里字符串常量或类字面量引用的对象
- 本地方法栈(JNI)中正在使用的 Native 引用的对象
为什么不用引用计数法?循环引用是硬伤
引用计数法给每个对象维护一个计数器,每次被引用就 +1,引用失效就 -1,归零即回收。听起来简单高效,但它无法处理两个或多个对象互相引用、但外部再无任何引用的情况。
- 比如 A.instance = B;B.instance = A;之后 A 和 B 的局部变量都置 null
- 此时 A 和 B 的计数器仍为 1(彼此引用),但它们已彻底脱离程序逻辑,无法被访问
- JVM 不会回收它们,造成内存泄漏——这就是循环引用漏洞
引用类型影响“死亡”的判定时机
即使对象不可达 GC Roots,不同引用类型也会让回收行为有差异:
- 强引用:只要存在,对象绝不会被回收;只有强引用全部断开,才可能进入“待回收”状态
- 软引用:内存充足时不回收;触发 Full GC 且内存仍不足时,才会回收关联对象
- 弱引用:只在下一次 GC 时就被回收,不管当前内存是否紧张
- 虚引用:仅用于接收对象被回收的通知,不影响对象生命周期
注意:两次标记与 finalize 的特殊处理
某些对象可能被第一次标记为“不可达”,但若它重写了 finalize() 方法且尚未执行过,JVM 会将它放入 F-Queue 队列,由低优先级线程尝试执行 finalize()。如果在此过程中对象重新与 GC Roots 建立了有效引用(比如把自己 this 赋给某个静态变量),它就能“复活”。否则,第二次标记后才真正确认死亡。
不过从 Java 9 开始,finalize() 已被标记为 deprecated,不建议使用,现代代码应通过 Cleaner 或 try-with-resources 管理资源。

















