# Java 中有哪些垃圾回收算法?
7 min read
Table of Contents
Java 中有哪些垃圾回收算法?
Java 垃圾回收算法详解
【核心定义】
Java 垃圾回收算法是 JVM 自动管理内存的核心机制,通过识别并回收不再被引用的对象来释放堆内存空间,防止内存泄漏。
【关键要点】
-
标记-清除算法(Mark-Sweep)
- 结论:最基础的垃圾回收算法,分为“标记”和“清除”两个阶段。
- 原理:首先标记所有从 GC Roots 可达的存活对象,然后清除未被标记的死亡对象。会产生内存碎片。
-
复制算法(Copying)
- 结论:将内存分为两块,每次只使用一块,存活对象复制到另一块。
- 原理:适用于对象存活率低的场景(如新生代)。没有碎片问题,但内存利用率只有 50%。
-
标记-整理算法(Mark-Compact)
- 结论:标记存活对象后,将所有存活对象向一端移动,然后清理边界外的内存。
- 原理:解决了内存碎片问题,适合老年代回收,但移动对象成本较高。
-
分代收集算法(Generational Collection)
- 结论:现代 JVM 的主流算法,基于对象生命周期特性采用不同策略。
- 原理:将堆分为新生代(Young Generation)和老年代(Old Generation),新生代使用复制算法(Eden + Survivor),老年代使用标记-清除或标记-整理。
【深度推导/细节】
分代收集的合理性推导
核心矛盾:大多数对象“朝生夕死”(弱分代假说),少数对象长期存活。
逻辑拆解:
- Step 1:统计表明 98% 的 Java 对象存活时间极短
- Step 2:将堆分为新生代(占 1/3)和老年代(占 2/3)
- Step 3:新生代采用复制算法(Eden:Survivor
= 8:1:1
)
- 新对象分配在 Eden 区
- Minor GC 时,Eden 存活对象复制到 Survivor0
- 再次 Minor GC,Eden 和 Survivor0 存活对象复制到 Survivor1
- 对象年龄计数器达到阈值(默认 15)晋升到老年代
- Step 4:老年代采用标记-整理/清除算法
- 当老年代空间不足时触发 Full GC
- Full GC 会 Stop The World,影响性能
关键数字解析
- 新生代比例 8:1:1 :基于对象死亡率统计的优化,90% 空间给新对象,10% 给存活对象复制
- 晋升年龄 15:对象头中年龄占 4 位,最大值为 15,是空间与精度的平衡
- 大对象直接进入老年代:避免在新生代频繁复制大对象(-XX:PretenureSizeThreshold )
【关联/对比】
不同垃圾收集器实现对比
| 收集器 | 算法特点 | 适用场景 | 线程关系 |
|---|---|---|---|
| Serial | 单线程标记-复制/整理 | 客户端模式 | Stop The World |
| Parallel Scavenge | 多线程复制/整理 | 吞吐量优先 | 并行收集 |
| CMS | 并发标记-清除 | 低延迟 | 并发收集 |
| G1 | 分Region标记-整理 | 平衡型 | 并发+并行 |
| ZGC | 染色指针并发整理 | 超低延迟 | 全并发 |
CMS vs G1 核心差异
- CMS:基于标记-清除,会产生碎片,需要 Full GC 整理
- G1:将堆划分为多个 Region,优先回收价值最高的 Region,可预测停顿时间
【线程安全与版本差异】
线程安全机制
- Stop The World:所有应用线程暂停,确保对象引用关系不变
- 安全点/安全区域:线程只能在特定位置暂停进行 GC
- 写屏障:维护卡表(Card Table),记录老年代到新生代的引用
版本演进
- Java 7/8:Parallel Scavenge + Parallel Old 为默认,CMS 作为低延迟选项
- Java 9+:G1 成为默认收集器
- Java 11:引入 ZGC(实验性),暂停时间 < 10ms
- Java 17:ZGC 和 Shenandoah 成为生产就绪
『面试官追问』
高频追问问题:
-
为什么新生代要用复制算法?
- 对象死亡率高,复制成本低
- 避免内存碎片,提高分配效率(指针碰撞)
-
G1 如何实现可预测的停顿时间?
- 维护 Region 的回收价值(回收空间/时间)
- 每个 GC 周期只回收部分 Region
- 通过 -XX:MaxGCPauseMillis 参数控制
-
三色标记法如何解决“对象消失”问题?
- 增量更新(CMS):记录新增的引用
- 原始快照(G1):记录删除的引用
- 通过写屏障实现
-
ZGC 的染色指针原理?
- 在 64 位指针中借用 4 位作为标记位
- 无需对象头标记,并发效率更高
- 支持 TB 级堆内存
性能调优关键参数:
- -Xms/-Xmx:堆初始/最大大小
- -XX:NewRatio :新生代/老年代比例
- -XX:SurvivorRatio :Eden/Survivor 比例
- -XX:MaxTenuringThreshold :晋升年龄阈值
- -XX:+UseG1GC:启用 G1 收集器
总结:Java 垃圾回收算法从基础算法演进到分代收集,再发展到现在的 Region 分区和全并发收集。选择合适算法需权衡吞吐量、延迟和内存开销,理解底层原理是性能调优的基础。