JVM垃圾回收算法考察重点是实际应用场景与设计原理:CMS为低延迟用标记-清除而非标记-整理以避免STW;G1通过Region分区和RSet实现混合回收;ZGC与Shenandoah借助读屏障和指针着色/转发实现并发整理。

Java面试中问到JVM垃圾回收算法,不是让你背诵“标记-清除”“复制”“标记-整理”这些名词,而是看你能不能说清:每种算法在什么内存区域、什么场景下被实际使用,以及为什么选它而不是别的。HotSpot虚拟机的GC实现和理论算法之间存在关键差异——比如“复制算法”在新生代用的是 Survivor 区的两个半区(S0 和 S1),但实际触发条件、晋升阈值、动态年龄判定等细节,才是面试官真正想确认你是否真懂的地方。
为什么CMS不使用标记-整理算法?
CMS(Concurrent Mark-Sweep)的目标是尽可能减少停顿时间,适用于老年代。它采用的是标记-清除算法,而非标记-整理,原因很实际:
-
标记-整理需要移动存活对象,意味着必须暂停所有应用线程(STW),违背CMS低延迟的设计初衷 -
标记-清除虽然会产生内存碎片,但CMS通过预留ConcGCThreads并发清理,且配合UseCMSCompactAtFullCollection(已废弃)或CMSScavengeBeforeRemark缓解问题 - 一旦碎片过多触发
Concurrent Mode Failure,JVM会退化为 Serial Old 进行 Full GC —— 此时才真正执行标记-整理,但代价是长时间STW
G1的Region分区如何改变传统算法边界?
G1不再严格区分年轻代/老年代的连续内存空间,而是把堆划分为多个固定大小的 Region(默认 1~32MB)。它的回收逻辑融合了多种策略:
- 年轻代回收(
Young GC)本质是复制算法:从所有EdenRegion + 部分SurvivorRegion 复制存活对象到新的Survivor或OldRegion - 混合回收(
Mixed GC)则按G1HeapRegionSize和G1MixedGCCountTarget动态选择一批OldRegion,对其中的存活对象执行复制(类似复制算法),同时合并空闲 Region - 它不追求“一次清理整个老年代”,而是基于
Remembered Set(RSet)快速识别跨 Region 引用,避免全堆扫描 —— 这才是 G1 降低 STW 的核心技术,不是算法本身多先进
ZGC 和 Shenandoah 的“并发整理”怎么绕过传统限制?
ZGC 和 Shenandoah 都宣称“几乎不 STW”,核心突破点不在算法名称,而在内存访问层面的改造:
立即学习“Java免费学习笔记(深入)”;
- 它们都使用
读屏障(Load Barrier)拦截对象引用访问,在对象被移动时同步更新引用,使得应用线程可与 GC 线程并发执行整理 - ZGC 采用着色指针(
colored pointers),将元数据直接编码在 64 位地址的高几位;Shenandoah 则依赖Brooks Pointer(在对象头前加转发指针) - 两者都不再需要“Stop-The-World 整理阶段”,但初始标记和最终标记仍有极短 STW(通常
- 注意:
-XX:+UseZGC要求 JDK 11+(生产就绪在 JDK 15+),而 Shenandoah 在 JDK 12+ 作为实验特性引入,JDK 15+ 默认启用
// 示例:JDK 17 启用 ZGC 的典型启动参数 java -XX:+UseZGC -Xmx8g -Xlog:gc*:file=gc.log:time -jar app.jar
真正容易被忽略的是:算法选择永远服务于 GC 目标(吞吐量 / 延迟 / 内存占用),而目标又受限于硬件(如大内存机器上 CMS 容易失败)、应用特征(如大量短期对象 vs 长期缓存对象)、甚至 JDK 版本(JDK 8 默认是 Parallel GC,JDK 11+ 默认是 G1)。死记硬背算法流程不如搞懂 HotSpot 中 CollectedHeap 子类(GenCollectedHeap、G1CollectedHeap、ZCollectedHeap)各自如何调度回收行为。










