一、G1垃圾收集器详解
1.1 核心设计思想
G1是一款面向服务器端应用的垃圾收集器,其核心设计目标是在保证高吞吐量的同时,提供可预测的停顿时间 。G1采用Region化分堆的内存布局,将整个Java堆划分为多个大小相等的独立区域(Region),最多支持2048个Region。
内存布局特点:
-
保留分代概念,但年轻代和老年代不再是物理隔离,而是由一系列Region组成
-
专门设立Humongous区处理大对象(超过Region 50%大小的对象)
-
Region角色可动态变化(年轻代↔老年代)
1.2 核心工作流程
1.2.1 GC运行阶段
-
初始标记(Initial Mark,STW)
标记GC Roots直接可达的对象,耗时极短。
-
并发标记(Concurrent Marking)
与用户线程并发执行,进行可达性分析。
-
最终标记(Remark,STW)
处理并发标记期间产生的变化,完成标记。
-
筛选回收(Cleanup,STW)
G1核心阶段 :根据用户设定的-XX:MaxGCPauseMillis(默认200ms),优先回收价值最高的Region,形成回收集(Collection Set)。
1.2.2 GC类型
-
Young GC
当Eden区满时触发,但G1会智能预测回收时间,仅在接近预设停顿阈值时才执行。
-
Mixed GC
老年代占用达阈值(默认45%)时触发,回收所有年轻代和部分老年代Region。
-
Full GC
单线程执行,全堆标记-清理-压缩,耗时长(Shenandoah已优化为多线程)。
1.3 关键参数调优
| 参数 | 默认值 | 说明 |
|---|---|---|
-XX:+UseG1GC |
- | 启用G1收集器 |
-XX:MaxGCPauseMillis |
200ms | 目标停顿时间 |
-XX:G1HeapRegionSize |
自动计算 | Region大小(1MB-32MB) |
-XX:InitiatingHeapOccupancyPercent |
45% | 触发Mixed GC的老年代阈值 |
-XX:G1MixedGCLiveThresholdPercent |
85% | Region回收存活对象阈值 |
-XX:G1HeapWastePercent |
5% | 空闲Region占比阈值 |
1.4 适用场景
-
堆内存 ≥ 8GB(推荐值)
-
要求停顿时间 ≤ 500ms
-
对象分配速率变化大
-
超过50%的堆内存被存活对象占用
典型案例 :Kafka等高并发消息系统,通过设置-XX:MaxGCPauseMillis=50ms,实现大内存下几乎无感知的GC停顿。
二、ZGC垃圾收集器详解
2.1 设计目标与特性
ZGC是JDK 11引入的实验性低延迟收集器,核心目标包括:
-
支持TB级堆内存
-
最大停顿时间 ≤ 10ms
-
停顿时间不随堆增大而增加
-
吞吐量损失 ≤ 15%
2.2 核心技术机制
2.2.1 内存布局
-
不分代(当前版本):简化实现,后续可能引入分代
-
Region分类:
-
小型Region:2MB,存放 <256KB 对象
-
中型Region:32MB,存放 256KB-4MB 对象
-
大型Region:动态大小,存放 ≥4MB 对象
-
2.2.2 颜色指针(Colored Pointers)
ZGC革命性地将GC元数据存储在指针而非对象头中:
text
64位指针结构:
┌──────────────┬─────┬─────┬─────┬─────┬──────────────────┐
│ 未使用(18位) │ Finalizable │ Remapped │ Marked1 │ Marked0 │ 对象地址(42位) │
└──────────────┴─────┴─────┴─────┴─────┴──────────────────┘
-
支持最大4TB堆(JDK 13扩展至16TB)
-
颜色位用于标记对象状态
2.2.3 读屏障(Load Barrier)
ZGC采用读屏障而非写屏障,在从堆读取引用时介入:
Object o = obj.fieldA; // 触发读屏障
当对象被移动时,屏障自动修正指针,实现指针自愈。
2.3 工作流程
-
并发标记:标记可达对象,更新指针标记位
-
并发预留重分配:确定需回收的Region集合
-
并发重分配:复制存活对象,建立转发表
-
并发重映射:修正堆中引用(合并到下次标记阶段)
2.4 现存问题与调优
2.4.1 主要问题
-
浮动垃圾:无分代导致全堆扫描,新对象需下次GC回收
-
内存开销:颜色指针和读屏障带来额外开销
2.4.2 调优参数
启用:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
| 参数类别 | 关键参数 |
|---|---|
| GC触发 | -XX:ZCollectionInterval、-XX:ZProactive |
| 内存分配 | -XX:AllocationSpikeTolerance |
| 诊断选项 | -XX:+UnlockDiagnosticVMOptions启用额外监控 |
三、G1 vs ZGC:对比与选型指南
3.1 核心差异对比
| 特性 | G1收集器 | ZGC收集器 |
|---|---|---|
| 引入版本 | JDK 7 | JDK 11(实验性) |
| 设计目标 | 平衡吞吐量与停顿 | 极致低延迟(≤10ms) |
| 分代模型 | 保留分代 | 单代(暂未分代) |
| 内存布局 | Region划分(固定大小) | Region划分(大/中/小三类) |
| 最大堆支持 | 约数十GB | 16TB(JDK 13+) |
| 停顿时间 | 可预测(通常100-200ms) | ≤10ms(与堆大小无关) |
| 核心技术 | 优先回收价值最高Region | 颜色指针+读屏障+指针自愈 |
| 适用场景 | 大内存、可接受百毫秒停顿 | 超大内存、要求亚毫秒停顿 |
3.2 选型决策树

3.3 通用选型建议
-
小内存(<4GB):Parallel Scavenge + Parallel Old
-
中等内存(4-8GB):ParNew + CMS(若注重响应)
-
大内存(8-100GB):G1(平衡吞吐与停顿)
-
超大内存(>100GB):ZGC(追求极致低延迟)
-
特殊场景:Shenandoah(OpenJDK,类似ZGC目标)
四、调优实战建议
4.1 G1调优重点
-
合理设置停顿目标 :
-XX:MaxGCPauseMillis不宜过小(建议100-300ms),否则导致回收不充分。 -
监控Mixed GC频率 :
调整
-XX:InitiatingHeapOccupancyPercent避免过早或过晚触发。 -
关注晋升速率 :
通过
-XX:G1NewSizePercent/MaxNewSizePercent控制年轻代大小,避免对象过快进入老年代。
4.2 ZGC注意事项
-
堆容量预留:由于浮动垃圾问题,建议预留20%-30%堆空间。
-
平台兼容性:确认操作系统和架构支持(Linux x64/AArch64、macOS、Windows 10+)。
-
吞吐量折衷:接受约4%-15%的吞吐损失。
4.3 通用优化原则
-
优先让JVM自动选择堆大小(不显式设置
-Xmx/-Xms) -
使用
-XX:+UseLargePages提升大内存性能 -
监控GC日志:
-Xlog:gc*(JDK 9+) -
结合应用特点:高分配速率应用需更大年轻代
五、未来展望
-
ZGC成熟化:预计未来版本将引入分代机制,解决浮动垃圾问题
-
Shenandoah:RedHat贡献的并发收集器,与ZGC目标类似,已在OpenJDK 12+可用
-
统一GC接口:JEP 304提议的GC接口标准化,便于切换和定制
总结
G1和ZGC代表了Java垃圾收集从「停顿不可控」到「可预测停顿」再到「极低停顿」的技术演进。G1适合大多数大内存应用场景,在吞吐量和停顿时间间取得良好平衡;ZGC则为超大堆和超低延迟需求提供了革命性解决方案。实际选型应结合应用特点、资源约束和业务需求,通过监控分析和渐进调优,找到最佳配置方案。