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

Java 并不使用引用计数法来判断对象是否可回收,JVM 实际采用的是可达性分析法。这是关键前提——引用类型本身(强、软、弱、虚)与引用计数法没有实现层面的绑定,它们是为配合可达性分析下的不同回收语义而设计的。
为什么 JVM 放弃了引用计数法
引用计数法逻辑直观:每个对象维护一个计数器,有新引用就加1,引用失效就减1;计数归零即判定为垃圾。但它存在两个致命缺陷:
- 无法处理循环引用:两个或多个对象互相持有对方的强引用,即使外部已无任何引用指向它们,计数器也不为0,导致内存永远无法释放
- 频繁更新计数器带来运行时开销:每次赋值、方法传参、作用域退出等都需同步增减,影响性能
主流 JVM(如 HotSpot)自 JDK 1.2 起就彻底摒弃该算法,改用基于 GC Roots 的可达性分析。
四种引用类型在可达性分析框架下的作用
它们不是独立的回收判定机制,而是对“对象是否可被回收”施加不同程度的约束,全部建立在可达性分析结果之上:
- 强引用:只要从 GC Roots 可达,且仅被强引用持有,该对象绝不会被回收——哪怕触发 OOM
- 软引用:GC 时若内存充足则保留;若即将 OOM,则把所有软引用对象列为可回收目标
- 弱引用:只要一次 GC 运行完成,无论内存是否紧张,所有仅被弱引用持有的对象都会被清除
- 虚引用:无法通过它获取对象实例,唯一用途是配合 ReferenceQueue,在对象被回收前收到通知
实际开发中容易混淆的点
很多人误以为“用了 WeakReference 就等于对象能被立刻回收”,其实不然:
- GC 是异步低优先级线程,WeakReference 对象可能存活多个 GC 周期才被清理
- 只要对象还被其他强/软引用持有着,WeakReference 就不会起作用
- WeakHashMap 的 key 是弱引用,但它的 value 仍是强引用——若不手动清理,value 可能造成内存泄露
内存泄露仍可能发生,与引用类型直接相关
即便有自动 GC,错误使用引用类型仍是常见泄露根源:
- 静态集合长期持有强引用(如 static Map
cache),对象无法进入不可达状态 - 监听器注册后未反注册,回调对象因被 listener 持有而持续存活
- ThreadLocal 使用不当,线程复用场景下残留的强引用阻止 value 回收
这些都不是 GC 机制失效,而是对象始终处于“可达状态”,不符合回收条件。











