Java随笔-GC收集器工作机制

一、GC 收集器分类总览

按回收区域分类

  • 年轻代收集器:Serial、ParNew、Parallel Scavenge
  • 老年代收集器:Serial Old、Parallel Old、CMS
  • 整堆收集器:G1、ZGC、Shenandoah

按工作模式分类

模式 特点 代表
串行(Serial) 单线程,全程 STW Serial
并行(Parallel) 多线程,全程 STW,吞吐量优先 Parallel Scavenge
并发(Concurrent) 部分阶段与应用线程并发,低停顿 CMS、G1
并发整理(Concurrent Compacting) 全阶段并发,几乎零停顿 ZGC、Shenandoah

2026 年 JDK 25 最新状态

  • G1:JEP 523 成为所有环境默认(包括受限环境),Remembered Set 内存降低 62%
  • ZGC:非分代模式已在 JDK 24 移除,仅保留分代 ZGC(-XX:+ZGenerational 不再需要)
  • Shenandoah:分代模式在 JDK 25 正式化,不再需要实验性标志

二、Serial / Serial Old(串行收集器)

适用场景

单核 CPU、客户端模式(Client VM)、内存较小(< 100MB)

参数

复制代码
-XX:+UseSerialGC

年轻代 Serial 回收流程(标记-复制算法)

STW 开始

├─ 单线程标记 Eden + From Survivor 中的存活对象

├─ 单线程复制存活对象到 To Survivor

├─ 清空 Eden + From Survivor

├─ From/To 交换角色

└─ 年龄达标(默认15)→ 晋升老年代

STW 结束

老年代 Serial Old 回收流程(标记-整理算法)

STW 开始

├─ 单线程标记所有存活对象

├─ 单线程整理:将存活对象向一端移动

└─ 清理边界外的内存

STW 结束

特点

  • 单线程回收,实现简单,无线程切换开销
  • STW 时间长(秒级),不适合服务端
  • 无内存碎片(Serial Old 使用标记-整理)
  • JDK 8 之前 Client 模式默认,现已被淘汰

三、Parallel / Parallel Old(并行收集器 / 吞吐量优先)

适用场景

后台计算、批处理、科学计算,追求最大吞吐量

参数

-XX:+UseParallelGC 或 -XX:+UseParallelOldGC

年轻代 Parallel Scavenge 回收流程(标记-复制算法)

STW 开始

├─ 多线程并行标记存活对象(线程数 = CPU 核心数)

├─ 多线程并行复制到 To Survivor

├─ 清空 Eden + From Survivor

└─ From/To 交换

STW 结束

老年代 Parallel Old 回收流程(标记-整理算法)

STW 开始

├─ 多线程并行标记存活对象

├─ 多线程并行整理(移动存活对象)

└─ 清理边界外内存

STW 结束

核心指标

吞吐量 = 用户代码运行时间 / (用户代码 + GC 时间)

特点

  • 多线程并行,充分利用多核 CPU
  • 吞吐量最高,适合 CPU 密集型任务
  • 可自适应调节(-XX:+UseAdaptiveSizePolicy)
  • STW 时间比 Serial 短,但仍需停顿
  • JDK 8 Server 模式默认(Parallel Scavenge + Parallel Old)

关键参数

参数 说明
-XX:MaxGCPauseMillis=<n> 目标最大停顿时间(JVM 会调整堆大小)
-XX:GCTimeRatio=<n> GC 时间占比目标(默认 99,即 1%)

四、CMS(Concurrent Mark Sweep,并发标记清除)

适用场景

互联网应用、Web 服务,追求低停顿(< 1秒)

参数

-XX:+UseConcMarkSweepGC

搭配年轻代:ParNew(-XX:+UseParNewGC)

CMS 回收流程(老年代,标记-清除算法)

阶段 1:初始标记(Initial Mark)------ STW,极短

  • 标记 GC Roots 直接关联的对象
  • 时间 < 10ms(与 Minor GC 的 STW 复用)

阶段 2:并发标记(Concurrent Mark)------ 与应用线程并发

  • 从 GC Roots 出发,遍历整个对象图
  • 不 STW,用户线程继续运行
  • 耗时较长(取决于堆大小和对象数量)
  • 使用增量更新(Incremental Update)记录新增引用

阶段 3:重新标记(Remark)------ STW,较短

  • 修正并发标记期间变动的标记
  • 处理增量更新队列中的引用变化
  • 时间比初始标记长,但远小于并发标记

阶段 4:并发清除(Concurrent Sweep)------ 与应用线程并发

  • 清理已标记为垃圾的对象
  • 不 STW,用户线程继续运行
  • 耗时较长,但不停顿

时间线示意

CMS 的致命缺陷

1. 内存碎片

  • 标记-清除算法产生大量碎片
  • 大对象分配失败,触发 Full GC
  • 缓解:-XX:+UseCMSCompactAtFullCollection(Full GC 时整理)

2. 浮动垃圾(Floating Garbage)

  • 并发清理期间产生的新垃圾,本轮无法回收
  • 需预留空间(-XX:CMSInitiatingOccupancyFraction,默认 68%)
  • 预留不足 → Concurrent Mode Failure

3. Concurrent Mode Failure(并发模式失败)------ 最严重

  • 并发标记期间,老年代空间被耗尽
  • CMS 无法继续,退化为 Serial Old 单线程 Full GC
  • 停顿时间剧增(秒级),用户体验极差

4. 对 CPU 敏感

  • 并发阶段占用 CPU 资源,降低吞吐量

CMS 参数

参数 说明
-XX:CMSInitiatingOccupancyFraction=<n> 老年代占用达 n% 时触发 CMS(默认 68)
-XX:+UseCMSInitiatingOccupancyOnly 只用设定阈值,不自动调整
-XX:+CMSParallelRemarkEnabled 并行重新标记
-XX:+CMSScavengeBeforeRemark 重新标记前触发 Minor GC

五、G1(Garbage First,区域化分代收集器)

适用场景

大堆内存(> 4GB)、可预测停顿时间、平衡吞吐量和延迟

参数

-XX:+UseG1GC(JDK 9+ 默认,JDK 25 JEP 523 成为所有环境默认)

G1 核心设计

1. Region 化内存布局

  • 将堆划分为多个 Region(1~32MB,默认 2048 个)
  • 每个 Region 动态扮演不同角色:
    • Eden Region:年轻代 Eden 区
    • Survivor Region:年轻代 Survivor 区
    • Old Region:老年代
    • Humongous Region:大对象(> Region 的 50%),连续多个 Region 组成,直接分配在老年代区域
    • Free Region:空闲区域
  • Region 角色可在不同回收周期之间改变

2. Garbage First 策略

  • 维护优先列表,按回收价值排序
  • 回收价值 = 垃圾占比 / 回收耗时
  • 每次选择回收价值最高的 Region,在有限时间内最大化回收量

3. 可预测停顿时间模型

  • -XX:MaxGCPauseMillis(默认 200ms)
  • G1 根据历史数据预测每个 Region 的回收耗时
  • 选择 Region 组合,确保总停顿时间 < 目标值

G1 回收流程 ------ Mixed GC

阶段 1:初始标记(Initial Marking)------ STW,很短

  • 标记 GC Roots 直接关联的对象
  • 修改 TAMS 指针(Top at Mark Start)
  • 复用 Young GC 的 STW(piggyback,不额外停顿)

阶段 2:根区域扫描(Root Region Scanning)------ 并发

  • 扫描 Survivor Region 中引用老年代的对象
  • 必须在下一次 Young GC 之前完成
  • 原因:Young GC 会移动 Survivor 对象,引用关系变化

阶段 3:并发标记(Concurrent Marking)------ 并发

  • 遍历整个堆,标记所有存活对象
  • 使用 SATB(Snapshot-At-The-Beginning) 算法
    • 标记开始时创建存活对象快照
    • 写屏障记录引用变更(旧值存入 SATB 队列)
    • 保守但正确:可能多标记一些垃圾,但不会漏标存活对象
  • 可被打断(Young GC 优先)
  • TAMS 指针:Region 内 TAMS 之上对象默认存活,不标记

阶段 4:重新标记(Remark)------ STW

  • 处理 SATB 队列中的引用变化
  • 计算各 Region 的存活对象比例
  • 时间较短

阶段 5:清理(Cleanup)------ 部分 STW

  • 统计各 Region 垃圾占比
  • 按回收价值排序
  • 选择回收价值最高的 Region 组成回收集(Collection Set)
  • 清空完全为空的 Region(非 STW)
  • 重置已清理 Region

阶段 6:混合回收(Mixed GC)------ STW

  • 复制 Eden + Survivor + 部分 Old Region 到新的 Region
  • 标记-整理 + 复制混合
  • 可多次执行(-XX:G1MixedGCCountTarget,默认 8)
    • 每次回收部分老年代,控制单次停顿时间
  • 清空旧 Region

G1 的 Remembered Set(RSet)

  • 每个 Region 维护一个 RSet,记录哪些 Region 引用了本 Region 的对象
  • 作用:避免全堆扫描,快速定位跨 Region 引用
  • 更新 :通过写屏障 + Dirty Card Queue 异步更新
    • 应用线程将引用变更写入线程私有的 Dirty Card Queue
    • Refinement 线程异步消费队列,更新 RSet
    • 队列满时应用线程参与排空(拖慢分配,自我保护)
  • JDK 25 优化:RSet 内存降低 62%(64GB 堆从 2GB 降至 0.75GB)

G1 的 Full GC

  • 触发条件:并发标记失败、Mixed GC 后空间仍不足
  • 使用单线程 Serial Old 算法(标记-整理)
  • 停顿时间长,应尽量避免

G1 参数

参数 说明
-XX:MaxGCPauseMillis=<n> 目标最大停顿时间(默认 200ms)
-XX:G1HeapRegionSize=<n> Region 大小(默认自动计算)
-XX:G1NewSizePercent=<n> 年轻代最小占比(默认 5%)
-XX:G1MaxNewSizePercent=<n> 年轻代最大占比(默认 60%)
-XX:InitiatingHeapOccupancyPercent=<n> 触发并发标记的堆占用率(默认 45%)
-XX:G1MixedGCCountTarget=<n> Mixed GC 目标次数(默认 8)
-XX:G1ReservePercent=<n> 预留内存比例(默认 10%)

六、ZGC(The Z Garbage Collector,低延迟收集器)

适用场景

超大堆内存(TB 级)、极低延迟要求(< 1ms)、金融交易系统

参数

-XX:+UseZGC(JDK 11+ 实验性,JDK 15+ 正式,JDK 24+ 仅分代模式)

ZGC 核心设计 ------ 三大黑科技

1. 染色指针(Colored Pointers)

  • 利用 64 位指针的高 4 位存储元数据(第 42-45 位)
  • 4 个标记位:
    • Marked0(M0):第 42 位,标记阶段 0 的存活对象
    • Marked1(M1):第 43 位,标记阶段 1 的存活对象
    • Remapped(R):第 44 位,对象已转移到新地址
    • Finalizable(F):第 45 位,对象有 finalize 方法
  • 优势
    • 无需在对象头中存储标记位,节省内存
    • 标记和转移可完全并发进行
    • 转移完成后无需立即更新所有引用
  • 限制:不支持压缩指针(Compressed Oops),内存开销 +15~30%

2. 读屏障(Load Barrier)

  • 在读取对象引用时自动插入的代码
  • 伪代码:
c 复制代码
Object load(Object* ref) {
    if (ref has Remapped bit) return ref;
    Object* new_ref = get_forwarded_address(ref);
    if (CAS(ref, new_ref)) return new_ref;
    return load(ref);  // 其他线程已更新,重读
}
  • 关键特性:
    • 仅在读操作时触发,写操作无需屏障
    • 开销极低(现代 CPU 可预测分支优化)
    • 实现无停顿并发转移:对象转移中仍可访问
  • 与 Shenandoah 的 Brooks Pointer 对比:
    • ZGC:读屏障 + 染色指针,无需对象头修改
    • Shenandoah:Brooks Pointer 在对象头中,需要额外空间

3. 多重映射(Multi-Mapping)

  • 将同一块物理内存映射到多个虚拟地址空间
  • 支持染色指针的不同视图(M0/M1/R 视图)
  • 对象转移时只需更新映射,无需复制数据(短期)

ZGC 回收流程

阶段 1:初始标记(Concurrent Mark 开始)------ STW,< 1ms

  • 标记 GC Roots 直接引用的对象

阶段 2:并发标记(Concurrent Mark)------ 并发

  • 遍历对象图,标记所有存活对象
  • 使用染色指针的 Marked0/Marked1 位交替标记
  • 读屏障处理并发引用变化

阶段 3:并发预备重定位(Concurrent Prepare for Relocate)------ 并发

  • 选择需要整理的 Region
  • 构建转发表(Forwarding Table)
  • 记录旧地址 → 新地址的映射

阶段 4:并发重定位(Concurrent Relocate)------ 并发

  • 将存活对象复制到新的 Region
  • 更新转发表(旧地址 → 新地址)
  • 读屏障自动转发访问(对象转移中仍可正常访问)
  • 这是 ZGC 的核心:并发转移,无停顿

阶段 5:并发重映射(Concurrent Remap)------ 并发

  • 更新所有指向旧地址的引用
  • 与下一次标记阶段合并(优化,避免重复扫描)
  • 读屏障逐步修正未更新的引用

时间线

分代 ZGC(Generational ZGC,JDK 21+)

背景

  • 非分代 ZGC 每次回收扫描整个堆,大堆 CPU 开销高
  • 无法利用"弱分代假说"(大部分对象朝生夕死)
  • 内存碎片问题仍存在

分代设计

  • 新生代:Eden + Survivor,复制算法,回收频率高
  • 老年代:标记-整理算法,回收频率低
  • 双标记位循环:新生代和老年代使用不同的 M0/M1 位
  • 跨代引用 :卡表(Card Table)记录老年代对新生代的引用
    • 新生代回收时只需扫描脏卡,无需扫描整个老年代
  • JDK 24 移除非分代模式,分代 ZGC 成为唯一模式

性能提升

  • 吞吐量提升 20~50%(核心提升)
  • CPU 使用率降低 30%
  • 内存碎片显著减少
  • 最大停顿时间 < 5ms(亚毫秒级)
  • 大堆场景 Full GC 概率大幅降低

ZGC 参数

参数 说明
-XX:+UseZGC 开启 ZGC
-XX:ZCollectionInterval=<n> 强制 GC 间隔(秒)
-XX:ZAllocationSpikeTolerance=<n> 分配速率容忍度(默认 2)
-XX:+UseLargePages 启用大页支持(强烈推荐)

七、Shenandoah(低延迟收集器,OpenJDK 特性)

适用场景

低延迟要求、Red Hat / OpenJDK 环境、内存受限容器

参数

-XX:+UseShenandoahGC(OpenJDK 12+,JDK 25 分代模式正式化)

Shenandoah 核心设计

1. Brooks Pointer(转发指针)

  • 每个对象头额外存储一个转发指针,初始指向自身
  • 对象转移时,将原对象的 Brooks 指针指向新对象
  • 所有对原对象的访问通过 Brooks 指针自动重定向
    • 读屏障检查 Brooks 指针,若已转移则转发
  • 转移完成后,后台线程逐步更新所有引用
    对象头结构:
    ┌────────────────────────────────────────┐
    │ mark_word │ 标记字 │
    │ klass_ptr │ 类指针 │
    │ brooks_ptr │ 转发指针(Shenandoah特有)│
    └────────────────────────────────────────┘

2. 连接矩阵(Connection Matrix)

  • 记录 Region 之间的引用关系
  • 优化跨代引用处理
  • 减少全堆扫描

3. 读写屏障

  • 读屏障:读取对象引用时检查 Brooks 指针
  • 写屏障:记录引用变化,更新卡表
  • 与 ZGC 对比:ZGC 只有读屏障,Shenandoah 有读写屏障

Shenandoah 回收流程

阶段 1:初始标记(Init Mark)------ STW,< 1ms

  • 标记 GC Roots 直接引用

阶段 2:并发标记(Concurrent Marking)------ 并发

  • 遍历对象图,标记存活对象

阶段 3:最终标记(Final Mark)------ STW,< 1ms

  • 处理并发标记期间的引用变化

阶段 4:并发清理(Concurrent Cleanup)------ 并发

  • 清理垃圾 Region

阶段 5:并发转移(Concurrent Evacuation)------ 并发

  • 复制存活对象到新的 Region
  • 更新 Brooks 指针(旧对象 → 新对象)
  • 读屏障自动转发访问

阶段 6:并发更新引用(Concurrent Update References)------ 并发

  • 更新所有指向旧地址的引用

分代 Shenandoah(JDK 24+ 实验性,JDK 25 正式)

  • 分代模式吞吐量提升 ~30%(相比单代)
  • 停顿时间 1~5ms(典型),偶尔 ~10ms
  • 支持压缩指针(Compressed Oops),内存开销比 ZGC 低
  • JDK 25 不再需要 -XX:+UnlockExperimentalVMOptions

Shenandoah 参数

参数 说明
-XX:+UseShenandoahGC 开启 Shenandoah
-XX:ShenandoahGCMode=generational 分代模式(JDK 25 默认)
-XX:ShenandoahGCHeuristics=<mode> 启发式模式(adaptive/static/compact)
-XX:+ShenandoahPacing pacing 控制分配速率

八、GC 收集器全维度对比表(2026 JDK 25)

维度 Serial Parallel CMS G1 ZGC(分代) Shenandoah(分代)
JDK 支持 所有 所有 所有 7+ 11+ 12+
默认状态 淘汰 JDK8默认 已废弃 JDK9+默认 可选 可选(OpenJDK)
回收区域 年轻/老 年轻/老 老年代 整堆 整堆 整堆
核心算法 复制/整理 复制/整理 标记-清除 Region混合 染色指针+读屏障 Brooks指针
工作模式 串行STW 并行STW 并发 并发+STW 全并发 全并发
典型停顿 <100ms <100ms <1s 20-200ms 0.1-0.5ms 1-5ms
最坏停顿 秒级 秒级 秒级 500ms <1ms ~10ms
吞吐量(vs G1) 最高 基准 -5~15% -5~10%
内存开销(vs G1) 基准 +15~30% +10~20%
CPU开销(vs G1) 基准 +5~10% +5~15%
压缩指针 支持 支持 支持 支持 不支持 支持
最佳堆范围 <2GB 任意 4-16GB 4-32GB 32GB-多TB 8-64GB
内存碎片 严重
适用场景 客户端 批处理 低停顿(JDK8) 通用 超低延迟 低延迟容器
2026 推荐 ✅ 默认 ✅ 延迟敏感 ✅ OpenJDK

九、GC 收集器选择决策树(2026)

JDK 版本建议

JDK 版本 推荐收集器
JDK 8 G1(-XX:+UseG1GC)或 CMS(遗留系统)
JDK 11~16 G1(默认),可选 ZGC(实验性)
JDK 17~21 G1(默认),ZGC(正式),Shenandoah(实验性)
JDK 22+ G1(默认),分代 ZGC (唯一模式),分代 Shenandoah(正式)

关键原则

  1. 先测后选:用实际负载测试不同收集器
  2. 监控指标:GC 停顿时间、吞吐量、内存碎片、CPU 使用率
  3. 渐进升级:JDK 8 → JDK 17/21 LTS,逐步验证
  4. 避免过度优化:G1 已满足大多数场景

十、关键概念深度解析

1. STW(Stop The World)

  • GC 期间暂停所有应用线程
  • 所有收集器都有 STW 阶段,只是长短不同
  • Serial/Parallel:全程 STW
  • CMS:初始标记 + 重新标记 STW
  • G1:初始标记 + 重新标记 + 混合回收 STW
  • ZGC/Shenandoah:仅初始标记 STW(< 1ms)

2. 吞吐量 vs 低延迟

指标 定义 追求 代表
吞吐量 用户代码运行时间 / 总时间 单位时间内完成更多工作 Parallel Scavenge
低延迟 单次 GC 停顿时间 响应速度,用户体验 ZGC、Shenandoah

3. 内存碎片

算法 碎片情况 代表
标记-清除 产生碎片 CMS
标记-整理 无碎片 Serial Old、Parallel Old、G1
复制 无碎片 Serial、ParNew、Parallel Scavenge

碎片导致大对象分配失败,触发 Full GC。

4. 写屏障(Write Barrier)vs 读屏障(Load Barrier)

屏障类型 触发时机 作用 代表
写屏障 对象引用写入时 记录引用变化 CMS(增量更新)、G1(SATB)、Shenandoah
读屏障 读取对象引用时 检查/转发对象状态 ZGC(染色指针)、Shenandoah(Brooks Pointer)

屏障有性能开销,但实现并发 GC 的关键。

5. 三色标记算法

颜色 含义 状态
白色 未访问 垃圾对象
灰色 已访问,引用未完全扫描 待处理
黑色 已访问,引用完全扫描 存活对象

并发标记问题:黑→白引用被切断,白对象被误回收

  • CMS 解决:增量更新(关注新增引用)
  • G1/ZGC 解决:SATB(关注旧值,保守但安全)

6. 弱分代假说(Weak Generational Hypothesis)

  • 大部分对象朝生夕死(在年轻代被回收)
  • 熬过多次 GC 的对象很难死亡(进入老年代)
  • 分代设计的理论基础:年轻代频繁回收,老年代低频回收
相关推荐
2601_962440841 小时前
计算机毕业设计之jsp旅游网站管理系统
java·开发语言·算法·课程设计·旅游·推荐算法
默默敲代码的徐哥儿1 小时前
八股文整理——后端
java·开发语言·spring boot·后端·学习
matlab代码1 小时前
基于matlab人脸门禁识别系统(可增加其它人脸图像)源码40期】
开发语言·matlab·人脸识别·人脸门禁
折哥的程序人生 · 物流技术专研2 小时前
第2篇:策略模式三大角色拆解与UML
java·设计模式·策略模式·uml·编程入门·行为型模式·扩充系列
李少兄2 小时前
Java CompletableFuture 解析(含常见面试题)
java·开发语言
名字还没想好☜2 小时前
Spring Boot 全局异常处理:@ControllerAdvice 实战
java·spring boot·后端·spring·异常处理
能摆一天是一天2 小时前
Spring ai vectorstore 使用本地模型导致只能匹配可行度为1.0的内容的解决方法笔记
java·笔记·spring
我命由我123452 小时前
方差(实例实操、与标准差的区别)
java·数据结构·算法·数据分析·java-ee·intellij-idea·idea
Java内核笔记2 小时前
Spring Security 源码解析(八)方法安全授权与自定义扩展:@EnableMethodSecurity、AOP、SpEL 全链路
java·后端