Java不采用引用计数法,因其无法处理循环引用:A与B互引时计数均不为0,即使脱离GC Roots也无法回收;JVM改用可达性分析,从GC Roots出发判定对象是否真正可达,天然规避该缺陷。

引用计数法无法解决循环引用问题,是因为它只看“谁还指着我”,不看“我指的那些对象是否真正可达”。只要两个或多个对象互相持有对方的引用,它们的引用计数就永远不为零,即使整个对象组已经脱离了程序的根(如栈、静态变量),JVM 也无法回收它们。
引用计数是怎么工作的
每个对象维护一个计数器,记录有多少引用指向它:
- 每新增一个引用(比如赋值给变量、放入集合),计数 +1
- 每销毁一个引用(比如变量离开作用域、被设为 null),计数 −1
- 计数归零时,对象立即被回收
循环引用为什么卡住计数器
假设有两个对象 A 和 B:
- A 中有一个字段 refToB 指向 B
- B 中有一个字段 refToA 指向 A
- 外部再没有任何变量引用 A 或 B(比如方法结束,局部变量消失)
此时:A 的引用计数至少为 1(来自 B.refToA),B 的引用计数也至少为 1(来自 A.refToB)。哪怕它们已不可达,计数器也不归零,无法回收——内存泄漏就发生了。
立即学习“Java免费学习笔记(深入)”;
主流 JVM 为什么不用引用计数法
Java 虚拟机(如 HotSpot)采用的是 可达性分析法,从 GC Roots(如线程栈帧中的局部变量、静态字段、JNI 引用等)出发,沿着引用链向下搜索。所有无法到达的对象才被视为垃圾。
- 循环引用的对象若与 GC Roots 断开,会被准确识别为不可达
- 无需实时更新计数,避免写屏障开销和多线程同步问题
- 天然支持更复杂的引用类型(如软引用、弱引用、虚引用)
引用计数法在 Java 中其实有“影子”存在
虽然不用于全局 GC,但某些场景仍用到类似思想:
- 某些 JVM 的部分优化(如逃逸分析后的栈上分配)会隐含引用生命周期判断
- Java 9+ 的 Cleaner 和 PhantomReference 配合 ReferenceQueue,依赖的是可达性分析,但清理逻辑本身需手动管理引用关系
- 第三方库或嵌入式 Java 环境(资源受限)偶有简化版引用计数实现,但必须配合周期性全量扫描来破循环


















