jvm通过可达性分析判断对象是否为垃圾:以gc roots(如栈帧局部变量、静态字段、常量池引用、jni本地引用)为起点,沿强引用链搜索,不可达对象即被标记为垃圾;该算法天然解决循环引用问题。

JVM 垃圾回收(GC)不是定时清扫,而是基于对象存活状态和内存压力动态触发的自动过程。它不依赖引用计数,而是以可达性分析为判断依据,结合分代管理和算法适配来高效回收内存。
怎么判断一个对象是垃圾
JVM 从一组固定的起点——GC Roots出发,沿着所有引用链向下搜索。能被这条路径触达的对象,就是“活着的”;反之,不可达的对象就被标记为垃圾。
常见的 GC Roots 包括:
- 当前线程栈帧中的局部变量和方法参数
- 类的静态字段(static 变量)所引用的对象
- 常量池中引用的对象
- JNI(Java Native Interface)中注册的本地引用
注意:循环引用不会干扰判断。哪怕 A 引用 B、B 又引用 A,只要它们都脱离了 GC Roots 的引用链,就会被一并回收。
为什么按代划分堆内存
JVM 把堆分为新生代(Young Gen)和老年代(Old Gen),元空间(Metaspace)单独管理类元数据。这种划分源于一个经验事实:绝大多数对象生命周期极短。
新生代进一步划为:
- Eden 区:新对象默认分配在这里
- Survivor 0 和 Survivor 1:用于存放经历一次 Minor GC 后仍存活的对象,两区交替使用
对象在 Survivor 区每熬过一次 GC,年龄 +1;达到阈值(默认 15 次,可调 -XX:MaxTenuringThreshold)后晋升到老年代。大对象(如长数组)可能直接进入老年代,避免频繁复制。
不同区域用什么算法回收
算法选择取决于区域特点:
- 新生代用复制算法:每次只处理 Eden + 一个 Survivor,把存活对象复制到另一个 Survivor 或直接晋升。操作快、无碎片,适合高频小规模回收
- 老年代多用标记-清除或标记-整理:对象存活率高,复制成本大。标记-清除会留下内存碎片;标记-整理则额外做压缩,腾出连续空间
- 元空间不归 GC 管理:它使用本地内存,类卸载由专门机制触发,不是传统意义上的“对象回收”
GC 是什么时候发生的
触发时机取决于内存使用情况和收集器策略:
- Minor GC:Eden 区满时触发,只清理新生代,停顿时间短但最频繁
- Mixed GC(如 G1):在回收新生代的同时,顺带清理部分老年代区域
- Full GC:整堆回收(新生代 + 老年代 + 元空间),通常因老年代空间不足、元空间耗尽、显式调用 System.gc()(不推荐)、CMS 失败等引发,停顿时间长,需尽量避免
现代收集器如 G1、ZGC、Shenandoah 支持并发标记和部分并发回收,大幅降低 STW(Stop-The-World)时间,但底层仍围绕可达性分析与分代逻辑展开。











