JVM G1 和 CMS 详解与对比

G1(Garbage-First)和 CMS(Concurrent Mark Sweep)都是 JVM 中针对老年代 的垃圾收集器,旨在解决传统 Serial Old、Parallel Old 收集器的STW(Stop-The-World)时间过长问题,适用于高并发、低延迟的应用场景。但二者的设计理念、实现机制和适用场景差异显著,本文将从核心特性、工作原理、优缺点及对比维度展开详解。

一、CMS 收集器详解

CMS 是第一款真正意义上的并发低延迟垃圾收集器 ,基于标记 - 清除(Mark-Sweep) 算法实现,核心目标是尽可能缩短 STW 时间,适用于对响应时间要求高的业务(如 Web 服务)。

1. 核心设计特点

  • 并发收集:大部分垃圾收集工作与用户线程并行执行,仅在少数阶段暂停用户线程。
  • 标记 - 清除算法 :只标记存活对象,直接清除死亡对象,不进行内存压缩
  • 老年代专属:需配合新生代的 Serial GC 或 ParNew GC 使用(通常搭配 ParNew + CMS)。

2. 工作流程(分为 6 个阶段,其中 4 个并发阶段)

(1)初始标记(Initial Mark)- STW
  • 目标 :标记GC Roots 直接关联的老年代对象(如新生代存活对象引用的老年代对象、全局静态变量引用的对象)。
  • 特点:暂停时间极短,仅扫描 GC Roots 直接关联的对象,无复杂遍历。
(2)并发标记(Concurrent Mark)- 并发
  • 目标 :从初始标记的对象出发,遍历整个老年代的对象引用图,标记所有存活对象。
  • 特点 :与用户线程并行执行,无 STW,但可能因用户线程修改对象引用产生浮动垃圾(Floating Garbage)(标记过程中产生的新垃圾,需等待下一次 GC 清理)。
(3)重新标记(Remark)- STW
  • 目标 :修正并发标记阶段因用户线程运行导致的标记遗漏(如对象引用被修改、新对象创建)。
  • 优化手段 :使用增量更新(Incremental Update) 算法,仅处理被修改的引用,缩短 STW 时间。
  • 特点:STW 时间比初始标记长,但远短于 Full GC;是 CMS 中主要的 STW 阶段之一。
(4)并发清除(Concurrent Sweep)- 并发
  • 目标:遍历老年代空间,清除所有未被标记的死亡对象,释放内存。
  • 特点 :与用户线程并行执行,无 STW;但因标记 - 清除算法,会产生内存碎片
(5)并发重置(Concurrent Reset)- 并发
  • 目标:重置 CMS 收集器的内部状态(如标记位、数据结构),为下一次 GC 做准备。

3. 关键参数

参数 作用
-XX:+UseConcMarkSweepGC 启用 CMS 收集器
-XX:+UseParNewGC 新生代使用 ParNew(与 CMS 配合的默认新生代收集器)
-XX:CMSInitiatingOccupancyFraction 设置 CMS 触发的老年代占用阈值(默认 92%,如设为 70 表示老年代用了 70% 时触发 CMS)
-XX:+CMSFullGCsBeforeCompaction 设置多少次 CMS 后执行一次内存压缩(默认 0,即每次 Full GC 都压缩)
-XX:+UseCMSCompactAtFullCollection 开启 Full GC 时的内存压缩(解决碎片问题)

4. 优缺点

优点
  • 低延迟:大部分阶段并发执行,STW 时间极短,适合对响应时间敏感的应用(如电商、金融交易系统)。
  • 成熟稳定:JDK 1.5 引入,经过长期迭代优化,在 JDK 8 中仍被广泛使用。
缺点
  • 内存碎片 :标记 - 清除算法不压缩内存,长期运行会产生大量碎片,导致分配大对象时触发 Full GC(STW 时间长)。
  • CPU 敏感:并发阶段需要占用 CPU 资源,在 CPU 核心数少的机器上(如 2 核),会与用户线程竞争 CPU,导致应用吞吐量下降。
  • 浮动垃圾 :并发标记阶段产生的垃圾无法被当前 GC 清理,需占用额外内存空间,若老年代空间不足,会触发Concurrent Mode Failure(并发模式失败),进而退化为 Serial Old GC(STW 时间更长)。
  • 仅支持老年代:需与新生代收集器配合,无法单独使用。

二、G1 收集器详解

G1(Garbage-First)是 JDK 7 引入、JDK 9 默认的垃圾收集器,设计目标是在高吞吐量的前提下,实现可预测的 STW 时间,适用于大内存(如 8GB 以上)、低延迟的应用场景。

1. 核心设计特点

  • 区域化内存管理 :将堆内存划分为多个大小相等的Region (区域,默认 1MB~32MB,可通过-XX:G1HeapRegionSize设置),新生代和老年代不再是连续的内存块,而是由多个 Region 组成(动态变化)。
  • 混合收集:同时处理新生代和老年代,无需单独的新生代收集器。
  • 标记 - 整理(Mark-Compact)+ 复制算法:在回收时,将存活对象复制到新的 Region,同时完成内存压缩,避免碎片。
  • 可预测的停顿 :用户可通过-XX:MaxGCPauseMillis(默认 200ms)设置目标 STW 时间,G1 会根据历史 GC 数据动态调整回收策略,尽量满足该目标。
  • 垃圾优先 :优先回收垃圾比例最高的 Region(即回收成本最低的 Region),以最大化回收效率。

2. 内存布局

G1 的堆内存被划分为一系列大小相同的 Region,主要包含以下类型:

  • Eden Region:新生代伊甸区,对象创建的主要区域。
  • Survivor Region:新生代幸存区,存储每次 GC 后存活的新生代对象。
  • Old Region:老年代区域,存储存活时间较长的对象。
  • Humongous Region :存储大对象(大小超过一个 Region 的 50%),直接划入老年代,避免大对象在新生代和老年代之间频繁移动。

3. 工作流程(分为 5 个阶段,含并发和 STW 阶段)

(1)初始标记(Initial Mark)- STW
  • 目标 :标记 GC Roots 直接关联的对象,同时标记新生代 Survivor 区引用的老年代对象
  • 触发时机:通常在新生代 GC(Young GC)时顺便执行,因此 STW 时间极短。
(2)并发标记(Concurrent Mark)- 并发
  • 目标 :从初始标记的对象出发,遍历整个堆的对象引用图,标记所有存活对象,并计算每个 Region 的垃圾比例(存活对象占比)。
  • 特点 :与用户线程并行执行,无 STW;过程中会记录对象的引用变化(使用SATB(Snapshot At The Beginning) 算法,解决并发标记的一致性问题)。
(3)最终标记(Final Mark)- STW
  • 目标 :处理并发标记阶段的引用更新日志,修正标记结果。
  • 特点 :使用多线程并行执行,会STW ,但时间较短。
(4)筛选回收(Live Data Counting and Evacuation)- STW
  • 目标
    1. 计算每个 Region 的垃圾比例,排序后选择垃圾比例最高的 Region(优先回收)。
    2. 将选中的 Region 中的存活对象复制到新的 Region(Eden/Survivor/Old),同时清除原 Region 的垃圾,完成内存压缩。
  • 特点 :可通过-XX:MaxGCPauseMillis控制回收的 Region 数量,从而控制 STW 时间;是 G1 中主要的 STW 阶段,但时间可预测。
(5)新生代收集(Young GC)
  • G1 的 Young GC 独立于混合收集,当 Eden Region 满时触发,将存活对象复制到 Survivor Region 或 Old Region,STW 时间短且可预测。

4. 关键参数

参数 作用
-XX:+UseG1GC 启用 G1 收集器
-XX:MaxGCPauseMillis 设置目标 STW 时间(默认 200ms,G1 会尽量满足)
-XX:G1HeapRegionSize 设置 Region 大小(1MB~32MB,需为 2 的幂)
-XX:G1NewSizePercent 新生代最小占比(默认 5%)
-XX:G1MaxNewSizePercent 新生代最大占比(默认 60%)
-XX:InitiatingHeapOccupancyPercent 触发并发标记的堆占用阈值(默认 45%)

5. 优缺点

优点
  • 可预测的 STW 时间:通过控制回收的 Region 数量,能满足用户设定的最大停顿时间,适合低延迟场景。
  • 无内存碎片:采用复制 + 标记 - 整理算法,回收时完成内存压缩,避免碎片问题。
  • 大内存友好:区域化管理使 G1 在大内存(如 16GB、32GB)下的 GC 效率远高于 CMS。
  • 统一管理新生代和老年代:无需配合其他收集器,简化配置。
  • 垃圾优先回收:优先回收垃圾多的 Region,回收效率更高。
缺点
  • 内存开销高:G1 需要维护每个 Region 的元数据(如垃圾比例、存活对象数),以及 SATB 算法的引用日志,内存开销约为堆内存的 10%~20%,比 CMS 高。
  • CPU 消耗大:并发标记和筛选回收阶段的线程调度、数据统计等操作会消耗更多 CPU 资源。
  • 小内存场景优势不明显:在小内存(如 4GB 以下)场景下,G1 的开销可能超过其优势,性能不如 Parallel GC 或 CMS。

三、G1 与 CMS 的核心对比

对比维度 CMS G1
设计目标 尽可能缩短 STW 时间,追求低延迟 可预测的 STW 时间,兼顾吞吐量和低延迟
内存管理 新生代和老年代连续内存块,固定分区 堆划分为多个 Region,动态分区(新生代 / 老年代由 Region 组成)
垃圾回收算法 标记 - 清除(Mark-Sweep) 标记 - 整理(Mark-Compact)+ 复制算法
内存碎片 严重(标记 - 清除不压缩) 无(回收时复制存活对象,完成压缩)
收集范围 仅老年代(需配合 ParNew) 新生代 + 老年代(统一管理)
STW 特性 STW 时间短但不可预测,可能因 Concurrent Mode Failure 导致长时间 STW STW 时间可通过参数设定,G1 动态调整回收策略以满足目标
大对象处理 直接在老年代分配,易产生碎片 用 Humongous Region 存储,回收时统一处理,无碎片
并发阶段 CPU 消耗 较低(仅并发标记和清除) 较高(需维护 Region 元数据、SATB 日志等)
内存开销 较低(约堆内存的 5%) 较高(约堆内存的 10%~20%)
适用场景 小内存(4GB 以下)、低延迟、CPU 核心数少的场景 大内存(8GB 以上)、可预测延迟、高并发的场景
JDK 支持 JDK 1.5 引入,JDK 9 被标记为废弃,JDK 14 移除 JDK 7 引入,JDK 9 成为默认收集器,持续优化
浮动垃圾处理 并发阶段产生的浮动垃圾需下次 GC 清理,易触发 Concurrent Mode Failure 同样存在浮动垃圾,但 Region 化管理降低了风险

四、选型建议

  1. 选择 CMS 的场景

    • 应用部署在小内存(4GB 以下)CPU 核心数少(2~4 核) 的机器上。
    • 瞬时延迟敏感,且能接受少量内存碎片和偶尔的 Full GC。
    • 基于 JDK 8 及以下版本,且无需大内存支持。
  2. 选择 G1 的场景

    • 应用部署在大内存(8GB 以上)多核 CPU的机器上(如云服务器、物理机)。
    • 要求可预测的 STW 时间(如电商秒杀、金融交易系统)。
    • 希望避免内存碎片,减少 Full GC 的发生。
    • 使用 JDK 9 及以上版本(G1 为默认收集器,优化更完善)。
  3. 其他选择

    • 若追求高吞吐量(如后台批处理任务),可选择 Parallel GC(Parallel Scavenge + Parallel Old)。
    • 若追求极致低延迟(如微服务、实时计算),可选择 ZGC 或 Shenandoah(JDK 11 + 支持,STW 时间可达毫秒级)。

五、总结

CMS 是第一代并发低延迟收集器 ,通过标记 - 清除算法实现了短 STW 时间,但存在内存碎片、CPU 敏感、并发模式失败等问题,适合小内存、低延迟的传统应用;G1 是新一代垃圾收集器,通过区域化管理、垃圾优先回收和可预测停顿,解决了 CMS 的核心痛点,更适合大内存、高并发的现代应用。

随着 JDK 版本的迭代,G1 已成为主流选择,而 CMS 逐渐被废弃。在实际项目中,应根据内存大小、CPU 核心数、延迟要求等因素选择合适的收集器,并通过压测和监控持续优化 GC 参数。

相关推荐
dddaidai1232 小时前
深入JVM(二):字节码文件的结构
java·开发语言·jvm
bruk_spp2 小时前
linux gpio获取
java·linux·服务器
SadSunset2 小时前
(5)spring的set注入
java·笔记·spring·架构
长而不宰2 小时前
使用 Canal实时监听数据库变化
java·数据库·spring boot
骚团长2 小时前
SQL server 配置管理器-SQL server 服务-远程过程调试失败 [0x800706be]-(Express LocalDB卸载掉)完美解决!
java·服务器·express
盼哥PyAI实验室2 小时前
Python多线程实战:12306抢票系统的并发处理优化
java·开发语言·python
风月歌2 小时前
小程序项目之农业电商服务系统源代码
java·mysql·毕业设计·ssm·源码
骚戴2 小时前
架构设计之道:构建高可用的大语言模型(LLM) Enterprise GenAI Gateway
java·人工智能·架构·大模型·gateway·api
TH_12 小时前
7、在线接口文档沟通
java