JVM放弃引用计数法的根本原因是无法解决循环引用导致的内存泄漏且并发更新计数器开销大;转而采用可达性分析法,以GC Roots为起点遍历判定对象存活,并结合强、软、弱、虚四种引用实现精细化回收控制。

JVM 没有采用引用计数法,根本原因在于它无法可靠判定对象是否真正“可回收”。该方法表面简单高效,但存在几个硬性缺陷,直接威胁内存管理的正确性与稳定性。
循环引用导致内存泄漏
当两个或多个对象互相持有引用(如 A→B、B→A),即使外部所有变量都已置为 null,它们的引用计数仍维持在 1 或更高。计数器无法归零,垃圾回收器便永远不会回收这些对象——尽管它们已彻底脱离程序逻辑,成为“不可达”的孤岛。
- 典型场景包括双向链表节点、父子树结构、事件监听器注册后未解绑、ORM 中的级联关联等
- 三对象环(A→B→C→A)甚至更长的环状结构,同样会让计数器“锁死”,问题不随对象数量增加而减弱,反而更隐蔽
写屏障开销与并发难题
每次赋值操作(如 obj.field = otherObj)都必须同步更新源对象和目标对象的计数器。这带来两类实际负担:
- 写屏障(Write Barrier)频繁触发,拖慢吞吐量,尤其在高频率对象创建/销毁场景下明显
- 多线程环境下需对计数器做原子操作或加锁,易引发竞争,降低 GC 效率和应用响应性
无法支持 Java 的引用语义体系
Java 提供强引用、软引用、弱引用、虚引用四种语义,用于实现缓存淘汰、资源清理、监控通知等高级功能。引用计数法天然只识别“是否存在引用”,无法区分引用强度,也无法在计数非零时按需释放对象。
- 例如:软引用对象应在内存不足时被回收,但引用计数法只要还有软引用指向它,计数就不为 0,无法触发回收
- 弱引用本意是“不阻止回收”,但在计数模型中,它仍会令计数器 +1,违背设计初衷
误判“存活”与“不可达”的本质矛盾
引用计数法只看局部关系,不理解对象在整个堆内存图中的连通性。它把“被谁引用”当作唯一依据,却忽略了“能否从 GC Roots 到达”这一全局事实。
- GC Roots(如栈中局部变量、静态字段、JNI 引用)才是程序活跃性的真正起点
- 可达性分析从 Roots 出发做图遍历,能准确识别整组不可达对象——哪怕它们内部环环相扣,也照收不误

















