Java垃圾回收器-G1、ZGC与Shenandoah
G1、ZGC和Shenandoah是Java平台针对不同场景的垃圾回收方案:G1适合中等延迟(几十毫秒)和几十GB级堆内存的应用,通过区域化设计平衡吞吐量与停顿时间;ZGC专为超低延迟(<10ms)和TB级内存优化,采用着色指针和并发处理实现几乎无停顿,但CPU开销较高;Shenandoah则通过Brooks指针和读屏障,在TB级内存下提供<10ms停顿,分代版本(JDK 22+)在吞吐量上表现更优。选择时需权衡延迟要求、内存规模、CPU资源及分配模式,ZGC适合实时系统和云原生场景,Shenandoah适合突发内存分配,而G1仍是传统应用的稳定之选。
一、垃圾回收器全景对比
| 特性 | G1 GC | ZGC | Shenandoah GC |
|---|---|---|---|
| 首次发布 | JDK 7 | JDK 11(实验性) | JDK 8+(实验性) |
| 正式稳定版 | JDK 7 | JDK 15 | JDK 11+(稳定) |
| 停顿时间 | 几十毫秒级 | <10ms(与堆大小无关) | <10ms (99.9%) |
| 最大堆大小 | 几十GB | TB级别(官方测试16TB) | TB级别 |
| 是否分代 | 是 | 否 | 传统:否,Generational:是 |
| 内存开销 | 中等 | 较高(着色指针) | 中等(Brooks指针) |
| CPU开销 | 中等 | 较高(并发处理) | 中等(需要三种屏障) |
| 核心技术 | 区域化 | 着色指针+读屏障 | Brooks指针+读屏障 |
| 适用场景 | 中等延迟、大内存应用 | 超大内存、超低延迟应用 | 大内存、低延迟应用 |
二、G1 GC:平衡吞吐量与延迟的主流选择
适用场景
- 企业级应用:如ERP、CRM系统等传统企业应用
- 中等规模大数据处理:内存在几十GB级别的应用
- 需要可预测GC停顿时间的应用:对延迟要求中等(几十毫秒级)
- 混合工作负载:同时处理CPU密集型和I/O密集型任务
为什么选择G1
- G1是JDK 7引入的垃圾收集器,经过长期验证,稳定性高
- 采用"区域化"设计,将堆划分为多个大小相等的Region
- 通过"Region"粒度控制,实现可预测的GC停顿时间
- 适合大多数需要平衡吞吐量和延迟的应用
代码示例
// G1 GC示例:模拟一个中等规模应用
public class G1Example {
public static void main(String[] args) {
// 模拟数据处理
int dataSize = 5000000; // 500万数据点
double[] data = new double[dataSize];
// 生成模拟数据
for (int i = 0; i < dataSize; i++) {
data[i] = Math.sin(i * 0.01) + Math.cos(i * 0.02);
}
// 模拟处理
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 释放部分内存
data = null;
System.out.println("G1 GC Example completed! Data size: " + dataSize);
}
}
运行命令
# 启用G1 GC,设置堆大小为8GB
javac G1Example.java
java -XX:+UseG1GC -Xmx8g -Xms8g -Xlog:gc*:file=/tmp/gc-g1.log -XX:+PrintGCDetails G1Example
G1 GC调优参数
# 设置目标停顿时间(毫秒)
-XX:MaxGCPauseMillis=200
# 设置堆内存保留百分比(用于GC期间备用内存)
-XX:G1ReservePercent=20
# 设置触发GC的堆占用百分比
-XX:InitiatingHeapOccupancyPercent=45
# 设置Region大小(1MB-32MB)
-XX:G1HeapRegionSize=4m
G1 GC不适用场景
- 对延迟要求极高(<10ms)的应用
- 需要TB级别堆内存的超大规模应用
- 低延迟金融交易系统
三、ZGC:超低延迟的TB级内存管理
适用场景
- 实时数据分析平台:如实时事件处理、流数据处理
- 高性能服务器:高并发Web服务、API网关
- 在线交易系统:需要毫秒级响应的交易系统
- 金融交易系统:高频交易、实时报价系统
- 云原生应用:需要管理TB级别堆内存的微服务
为什么选择ZGC
- 超低停顿:停顿时间<10ms,且与堆大小无关
- TB级内存支持:官方测试支持16TB堆内存
- 统一堆管理:无需分代,简化内存管理
- 并发处理:大部分GC工作并发执行,几乎无STW
代码示例
// ZGC示例:模拟一个实时数据处理系统
public class ZGCExample {
public static void main(String[] args) {
// 模拟实时数据流处理
int dataPointsPerSecond = 100000;
int totalDuration = 30; // 30秒
for (int i = 0; i < totalDuration; i++) {
// 每秒处理10万条数据
for (int j = 0; j < dataPointsPerSecond; j++) {
// 模拟数据处理
double value = Math.random() * 100;
String data = "Real-time Data: " + value;
// 处理数据(模拟业务逻辑)
processRealTimeData(data);
}
try {
Thread.sleep(1000); // 每秒处理一次
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Processed " + (i + 1) + " seconds of real-time data");
}
System.out.println("ZGC Example completed! Total data processed: " + (dataPointsPerSecond * totalDuration));
}
private static void processRealTimeData(String data) {
// 模拟数据处理逻辑
// 实际应用中可能是复杂的业务逻辑
}
}
运行命令
# 启用ZGC,设置堆大小为16GB(适合TB级应用)
javac ZGCExample.java
java -XX:+UseZGC -Xmx16g -Xms16g -Xlog:gc*:file=/tmp/gc-zgc.log -XX:+PrintGCDetails ZGCExample
ZGC调优参数
# 并发GC线程数(建议CPU核心数的1/8到1/4)
-XX:ConcGCThreads=4
# 并行GC线程数(建议CPU核心数)
-XX:ParallelGCThreads=16
# 内存分配峰值容忍度(默认5)
-XX:ZAllocationSpikeTolerance=8
# 未提交内存延迟(单位:毫秒,可选)
-XX:ZUncommitDelay=3000
ZGC典型场景优化示例
# 金融交易系统优化配置
java -XX:+UseZGC -Xmx32g -Xms32g \
-XX:ConcGCThreads=8 \
-XX:ParallelGCThreads=32 \
-XX:ZAllocationSpikeTolerance=10 \
-Xlog:gc*:file=/var/log/gc-zgc.log \
-XX:+PrintGCDetails \
-XX:+UnlockExperimentalVMOptions \
TradingSystemApp
ZGC不适用场景
- CPU敏感环境(ZGC并发处理会占用较多CPU资源)
- 对内存占用非常敏感的应用(ZGC有额外的内存开销)
四、Shenandoah GC:分代优化的低延迟选择
适用场景
- 大内存服务:需要管理TB级别堆内存的云原生应用
- 低延迟系统:金融交易、实时推荐系统
- 突发分配应用:内存分配模式不规则的应用
- 分代优化场景:Generational Shenandoah(JDK 22+)
为什么选择Shenandoah
- 低延迟:停顿时间<10ms (99.9%)
- 分代支持:Generational Shenandoah(JDK 22+)提供分代优化
- 内存效率:相比ZGC,内存开销更小
- 平台支持:主要支持Linux 64位
代码示例
// Shenandoah GC示例:模拟一个突发内存分配应用
public class ShenandoahExample {
public static void main(String[] args) {
// 模拟突发内存分配场景
int totalIterations = 10;
int maxObjectsPerIteration = 1000000;
for (int i = 0; i < totalIterations; i++) {
int objectsToCreate = (int)(Math.random() * maxObjectsPerIteration) + 1;
// 创建随机数量的对象
String[] strings = new String[objectsToCreate];
for (int j = 0; j < objectsToCreate; j++) {
strings[j] = "Shenandoah Example: " + j;
}
// 模拟处理
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 释放对象
strings = null;
System.out.println("Iteration " + (i + 1) + " completed. Created " + objectsToCreate + " objects");
}
System.out.println("Shenandoah Example completed!");
}
}
运行命令(传统Shenandoah)
# 启用Shenandoah GC,设置堆大小为2GB
javac ShenandoahExample.java
java -XX:+UseShenandoahGC -Xmx2g -Xms2g -Xlog:gc*:file=/tmp/gc-shenandoah.log -XX:+PrintGCDetails ShenandoahExample
运行命令(Generational Shenandoah,JDK 22+)
# 启用分代Shenandoah GC,设置年轻代大小
javac ShenandoahExample.java
java -XX:+UseShenandoahGC -XX:+UseShenandoahGenerationalGC \
-XX:YoungGenerationSize=2g -XX:MaxYoungGenerationSize=4g \
-Xmx16g -Xms16g \
-Xlog:gc*:file=/tmp/gc-shenandoah-gen.log -XX:+PrintGCDetails \
ShenandoahExample
Shenandoah GC调优参数
# 设置GC策略(aggressive, adaptive, balanced)
-XX:ShenandoahGCHeuristics=aggressive
# 设置最大停顿时间目标(纳秒)
-XX:ShenandoahPauseNanos=5000000 # 5ms
# 启用可中断的预清理
-XX:ShenandoahAbortablePrecleaning=on
# 混合回收控制(老年代区域存活对象阈值)
-XX:MixedGCLiveThresholdPercent=85
# 混合回收轮次
-XX:MixedGCCountTarget=8
# 年轻代大小调整
-XX:YoungGenerationSize=2g
-XX:MaxYoungGenerationSize=4g
# 晋升阈值调整
-XX:InitialTenuringThreshold=5
-XX:MaxTenuringThreshold=15
Shenandoah典型场景优化示例
# 金融交易系统优化配置(Generational Shenandoah)
java -XX:+UseShenandoahGC -XX:+UseShenandoahGenerationalGC \
-XX:YoungGenerationSize=2g -XX:MaxYoungGenerationSize=4g \
-XX:ShenandoahGCHeuristics=aggressive \
-XX:ShenandoahPauseNanos=5000000 \
-Xmx16g -Xms16g \
-Xlog:gc*:file=/var/log/gc-shenandoah.log \
-XX:+PrintGCDetails \
TradingSystemApp
Shenandoah不适用场景
- 对延迟要求比ZGC还低的场景(ZGC停顿时间更短)
- 需要非常低的CPU开销的应用(Shenandoah需要三种屏障)
五、如何选择最适合的垃圾回收器
选择决策树
-
应用对延迟的要求是什么?
- 如果需要<10ms的停顿,优先考虑ZGC或Shenandoah
- 如果需要几十毫秒的停顿,G1是良好选择
-
应用需要处理的内存大小是多少?
- 小于10GB:G1或ZGC
- 10GB-100GB:G1或ZGC
- 100GB+:ZGC或Shenandoah
-
应用的内存分配模式如何?
- 规则分配:G1或ZGC
- 不规则分配:Shenandoah(特别是Generational Shenandoah)
-
CPU资源是否有限制?
- CPU敏感环境:G1或Shenandoah(ZGC并发处理CPU开销较高)
- CPU充足:ZGC或Shenandoah
实用选择指南
| 应用类型 | 推荐GC | 原因 |
|---|---|---|
| 传统企业应用(ERP/CRM) | G1 GC | 稳定性高,平衡吞吐量与延迟 |
| 中等规模大数据处理 | G1 GC | 可预测的停顿时间,内存在几十GB级别 |
| 实时数据分析平台 | ZGC | 低延迟、TB级内存支持 |
| 高性能Web服务 | ZGC | 高并发场景下低停顿 |
| 金融交易系统 | ZGC | <10ms停顿,确保交易流畅 |
| 云原生微服务 | ZGC或Shenandoah | TB级内存管理能力 |
| 突发内存分配应用 | Shenandoah GC | 适合不规则内存分配模式 |
| 分代优化需求应用 | Generational Shenandoah | JDK 22+支持,吞吐量优势 |
六、总结与建议
-
ZGC是当前超低延迟场景的最佳选择,尤其适合TB级内存应用和对延迟要求极高的场景。如果你的应用需要<10ms的停顿时间,且内存需求在TB级别,ZGC是首选。
-
Shenandoah GC在分代优化后(JDK 22+)将成为ZGC的有力竞争者,特别是对于需要分代收集的低延迟应用。Generational Shenandoah有望在吞吐量上取得优势。
-
G1 GC仍然是大多数应用的稳定选择,尤其适合中等规模、延迟要求不极端的应用。它经过长期验证,稳定性高,是大多数Java应用的默认选择。
-
在实际选择时,务必进行性能基准测试:不要仅依赖理论数据,应根据你的具体应用进行压力测试和GC日志分析。
-
ZGC和Shenandoah都支持JDK 11+,建议升级到最新JDK版本以获得最佳性能。
小贴士:如果你正在从G1迁移到ZGC或Shenandoah,建议先在测试环境中进行性能基准测试,因为不同GC对应用性能的影响可能很大。ZGC在TB级内存上表现优异,但可能会增加CPU使用率;Shenandoah在分代优化后,吞吐量可能优于ZGC。
- 感谢你赐予我前进的力量

