2. JVM 内存模型与 GC 基础:堆内存结构、新生代与老年代、GC 触发条件、STW 概念

在深入排查 CMS GC 异常之前,必须建立对 JVM 内存模型与垃圾回收机制的扎实理解。本章将聚焦于堆内存的核心分区、对象晋升路径、GC 触发条件以及 Stop-The-World(STW)这一关键概念,为后续分析 CMS 并发失败、晋升失败等异常奠定理论基础。

2.1 堆内存结构:分代设计的核心

JVM 堆是 GC 管理的主要区域,采用分代收集策略,基于“大部分对象朝生夕死”的统计规律,将堆划分为新生代(Young Generation)和老年代(Old Generation)。

核心原则:新生代存放生命周期短的对象,老年代存放长期存活的对象。不同代采用不同的回收算法,以平衡吞吐量与延迟。
JVM 堆内存结构(分代模型) 新生代 (Young Generation) Eden 区 对象首次分配 S0 From S1 To 老年代 (Old Generation) 存放长期存活对象 CMS 主要回收区域 元空间 (Metaspace) — 存储类元数据,不受堆大小限制 晋升 说明:新生代默认占堆 1/3,老年代占 2/3。Eden : S0 : S1 默认比例 8:1:1。

2.2 新生代与老年代:对象生命周期管理

2.2.1 新生代 (Young Generation)

  • Eden 区:大多数对象首先在 Eden 区分配。当 Eden 区空间不足时,触发 Minor GC。
  • Survivor 区(S0 / S1):用于存放 Minor GC 后存活的对象。S0 和 S1 大小相等,同一时刻只有一个处于活跃状态(From),另一个为空(To)。
  • 对象晋升:每经历一次 Minor GC 仍存活的对象,年龄加 1。当年龄达到阈值(默认 15,可通过 -XX:MaxTenuringThreshold 设置)时,晋升到老年代。

2.2.2 老年代 (Old Generation)

  • 存放长期存活的对象,以及从新生代晋升的大对象。
  • 老年代空间不足时触发 Major GC 或 Full GC。
  • 在 CMS 收集器中,老年代是并发收集的主要目标区域。
调优提示:如果频繁发生对象晋升失败(Promotion Failed),通常意味着老年代空间不足或 Survivor 区过小,导致对象提前进入老年代。可通过调整 -Xmn(新生代大小)或 -XX:SurvivorRatio 来优化。

2.3 GC 触发条件

不同 GC 事件的触发条件如下表所示:

GC 类型 触发条件 影响范围 典型耗时
Minor GC Eden 区空间不足 新生代(Eden + Survivor) 几毫秒 ~ 几十毫秒
Major GC 老年代空间不足 老年代 几百毫秒 ~ 几秒
Full GC 老年代空间不足、元空间不足、System.gc() 调用、CMS 并发模式失败 整个堆 + 元空间 几秒 ~ 几十秒
CMS 并发模式失败 CMS 回收期间老年代被填满 老年代(退化为 Serial Old 单线程回收) 可能长达几十秒
注意:在 CMS 收集器中,并发模式失败(Concurrent Mode Failure)是最严重的异常之一。它意味着 CMS 在并发标记或并发清理阶段,老年代空间已被耗尽,导致 JVM 暂停所有应用线程,使用 Serial Old 收集器进行单线程 Full GC,造成长时间的 STW。

2.4 Stop-The-World (STW) 概念

STW 是垃圾回收过程中,JVM 暂停所有应用线程(除 GC 线程外)的行为。在 STW 期间,应用程序完全停止响应,用户请求被阻塞。

2.4.1 STW 的必然性

  • 根节点枚举:GC 需要从 GC Roots(栈帧中的局部变量、静态变量等)出发,标记所有存活对象。此过程必须在一个一致性的快照中进行,否则对象引用关系会不断变化,导致标记结果不准确。
  • 安全点(Safepoint):JVM 只在所有线程到达安全点时才能进入 STW。安全点通常位于循环末尾、方法返回、异常抛出等位置。

2.4.2 STW 对应用的影响

指标 说明 典型阈值
暂停时间 单次 STW 的持续时间 CMS 目标 < 200ms;G1 目标 < 100ms
暂停频率 单位时间内 STW 发生的次数 通常每分钟几次到几十次
吞吐量 应用线程运行时间占总时间的比例 通常要求 > 95%
CMS 的设计哲学:CMS 通过将大部分 GC 工作与应用程序并发执行,来减少 STW 时间。但 CMS 仍然需要两次短暂的 STW:初始标记(Initial Mark)和重新标记(Remark)。初始标记仅标记 GC Roots 直接引用的对象,耗时极短;重新标记修正并发标记期间发生变化的对象引用,耗时稍长。
CMS GC 阶段与 STW 示意图 初始标记 STW 并发标记 与应用线程并发 重新标记 STW 并发清理 与应用线程并发 重置 CMS 的 STW 阶段 初始标记:标记 GC Roots 直接引用的对象,耗时极短(通常 < 1ms)。 重新标记:修正并发标记期间因应用线程运行而发生变化的对象引用,耗时稍长(通常几毫秒到几十毫秒)。

2.5 关键参数速查表

参数 默认值 说明
-Xms 物理内存的 1/64 堆初始大小
-Xmx 物理内存的 1/4 堆最大大小
-Xmn 堆的 1/3 新生代大小
-XX:SurvivorRatio 8 Eden : Survivor 比例(如 8:1:1)
-XX:MaxTenuringThreshold 15 对象晋升老年代的年龄阈值
-XX:+UseConcMarkSweepGC 关闭 启用 CMS 收集器
-XX:CMSInitiatingOccupancyFraction 68 老年代使用率达到该百分比时触发 CMS 回收
实战建议:在数据库迁移场景中,由于数据量激增或连接池膨胀,老年代占用率可能快速上升。建议将 -XX:CMSInitiatingOccupancyFraction 适当调低(如 60%),提前触发 CMS 回收,避免并发模式失败。