jvm通过可达性分析判定对象存活,以gc roots为起点沿引用链遍历,不可达对象即为垃圾;gc roots包括虚拟机栈局部变量、方法区静态变量与常量、jni引用及被锁对象;结合分代假设与四种引用类型,实现高效精准回收。

JVM 实现高效垃圾识别,核心在于准确、快速、低开销地判定对象是否存活。它不依赖简单计数,而是采用一套兼顾理论严谨性与工程可行性的机制——以可达性分析为唯一判定依据,配合精确定义的 GC Roots 集合和分代假设,在保证零误判的前提下大幅压缩搜索范围与计算成本。
可达性分析是唯一可靠的基础
JVM 放弃了引用计数法,因其无法处理循环引用(如两个对象互相持有 instance 字段但外部已无引用),会导致内存泄漏。取而代之的是可达性分析算法:
- 从一组严格限定的“根对象”(GC Roots)出发,沿引用链向下遍历所有可到达的对象
- 未被任何引用链触及的对象,即被判定为“不可达”,确认为垃圾
- 该方法天然规避循环引用问题:只要整个循环体与 GC Roots 断开,整组对象都会被一并回收
GC Roots 的范围小且稳定,极大提升效率
不是所有对象都能当根——JVM 只将生命周期明确、访问路径固定、数量可控的几类对象纳入 Roots 集合:
- 虚拟机栈中每个线程的栈帧里,正在使用的局部变量、方法参数、临时变量所引用的对象
- 方法区中静态字段(static)引用的对象
- 方法区中常量池(如字符串字面量、类名符号)引用的对象
- 本地方法栈中 JNI(Native 方法)持有的 Java 对象引用
- 被 synchronized 锁住的对象(表明正被线程使用)
这些 Roots 数量有限、位置集中、查找快,避免了全堆扫描,是高效识别的前提。
分代假设让识别过程“按需聚焦”
JVM 基于经验观察(绝大多数对象朝生暮死),把堆划分为年轻代和老年代,并据此优化识别策略:
- 年轻代 GC(Minor GC)只检查年轻代内对象是否可达,且 Roots 不仅包含全局 Roots,还包含老年代中指向年轻代的引用(称为“跨代引用”)
- 通过卡表(Card Table)等技术,只标记可能有跨代引用的老年代内存页,避免每次扫描整个老年代
- 老年代 GC(Major/Full GC)虽需更广范围,但触发频率远低于 Minor GC,整体识别开销仍可控
引用类型分级进一步细化识别粒度
强、软、弱、虚四种引用类型,让 JVM 在“是否算存活”上有了语义级判断能力:
- 强引用对象:只要存在,就绝不回收;这是默认行为
- 软引用对象:仅在内存不足时才被视作可回收目标,适合缓存场景
- 弱引用对象:GC 时直接视为不可达,无需等待内存压力
- 虚引用:不阻止回收,仅用于在对象被回收前收到通知
这种分级使识别逻辑不再只是“是/否”,而是“何时可否”,提升了资源利用的灵活性和精准度。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











