Java判断对象可回收的核心是可达性分析算法而非引用计数法,因后者无法解决循环引用导致的内存泄漏——A与B互引时计数器恒不为0,而前者从GC Roots出发遍历,天然规避该问题。

Java 中判断对象是否可回收,核心靠的是可达性分析算法,不是引用计数法。主流 JVM(如 HotSpot)完全不采用引用计数,因为它无法处理循环引用,会导致内存泄漏。
为什么不用引用计数法
每个对象维护一个计数器,被引用就 +1,引用失效就 -1,归零即回收。听起来简单,但实际有硬伤:
- 两个对象互相持有对方引用(比如 A.b = B,B.a = A),即使外部所有变量都置为 null,它们的计数器仍为 1,永远不为 0
- 无法区分“被引用”和“被有效使用”,只要存在引用就计数,缺乏语义层面的活跃性判断
- JVM 需要实时更新计数器,多线程下还要加锁,开销大、不高效
可达性分析怎么工作
它从一组“必须存活”的起点出发,顺着引用链向下扫描。凡是从这些起点不可达的对象,就判定为可回收。
- 起点就是 GC Roots:包括虚拟机栈中正在使用的局部变量、方法区的静态变量、常量池中的常量、本地方法栈中的 JNI 引用
- 引用链可以是直接引用(如 obj.field),也可以是间接引用(obj.field.next),只要能连到 GC Roots 就算存活
- 一次不可达不代表立刻回收:如果对象覆盖了 finalize() 方法,会先入 F-Queue 队列,执行一次 finalize(仅一次),之后再判断是否仍不可达——两次标记后才真正回收
四种引用类型影响回收时机
即使对象不可达 GC Roots,不同引用类型也会改变它的命运:
- 强引用(new Object()):最常见,只要强引用存在,就不会被回收
- 软引用(SoftReference):内存不足时才回收,适合缓存场景
- 弱引用(WeakReference):GC 时无论内存是否充足都会回收
- 虚引用(PhantomReference):唯一不能通过引用来获取对象的类型,仅用于在对象被回收前收到系统通知
面试容易踩的坑
回答时注意避开几个高频错误点:
- 不要说“JVM 用引用计数法”,这是错的;要说“HotSpot 等主流实现用的是可达性分析”
- 别把 GC Roots 说成“所有静态变量”,要强调是“方法区中类的静态属性引用的对象”,常量、JNI 引用等也要提全
- finalize 不是析构函数,不保证执行,也不建议重写;现代 Java(9+)已标记为 deprecated
- 对象死亡判定只是第一步,后续还涉及分代回收策略(新生代用复制,老年代用标记整理/清除)

















