如何判断对象是否可回收?引用计数与可达性分析有何区别?

结论先行:主流 JVM(HotSpot)使用可达性分析(GC Roots Tracing):从 GC Roots 出发沿引用链向下搜索,未被任何引用链触达的对象判定为不可达、可回收。引用计数因无法解决循环引用且维护计数有额外开销,已不被主流实现采用。

两种判定算法对比

对比项引用计数可达性分析
原理被引用一次计数 +1,失效 -1,归零即回收从 GC Roots 遍历引用链,不可达即回收
循环引用无法处理(A↔B 互指则计数永不归零)天然免疫,整环一起回收
额外开销每次引用赋值/变化都要同步计数只在 GC 停顿期遍历一次
使用现状部分语言辅助使用,JVM 不采用HotSpot 默认方案

哪些对象能当 GC Roots

  • 虚拟机栈(栈帧局部变量表)中引用的对象;
  • 方法区静态属性、常量引用的对象;
  • 本地方法栈中 JNI 引用的对象;
  • 被同步锁(synchronized)持有的对象等;
  • 扩展补充:运行时常量池引用的对象、活跃线程对象、JNI 全局引用也常被归入 GC Roots。
public class CycleRefDemo {
    static class Node { Node next; }

    public static void main(String[] args) {
        Node a = new Node();
        Node b = new Node();
        a.next = b;   // 构造循环引用
        b.next = a;
        a = null;
        b = null;
        // 引用计数下两者计数恒为 1 → 泄漏;
        // 可达性分析下整环均不可达 → 可正常回收
    }
}

常见追问 / 记忆点

  • 记忆点:答“如何判断可回收”先答可达性分析,再补一句引用计数死于循环引用。
  • 追问:强/软/弱/虚四种引用影响回收时机:软引用内存不足才回收、弱引用下次 GC 就回收、虚引用配合堆外内存回收。
  • 追问:能当 GC Roots 的引用一定活在栈、静态区或 JNI 中,面试举这三类基本够用。
笔记加载中…