G1、ZGC和Shenandoah是Java平台针对不同场景的垃圾回收方案:G1适合中等延迟(几十毫秒)和几十GB级堆内存的应用,通过区域化设计平衡吞吐量与停顿时间;ZGC专为超低延迟(<10ms)和TB级内存优化,采用着色指针和并发处理实现几乎无停顿,但CPU开销较高;Shenandoah则通过Brooks指针和读屏障,在TB级内存下提供<10ms停顿,分代版本(JDK 22+)在吞吐量上表现更优。选择时需权衡延迟要求、内存规模、CPU资源及分配模式,ZGC适合实时系统和云原生场景,Shenandoah适合突发内存分配,而G1仍是传统应用的稳定之选。

一、垃圾回收器全景对比

特性G1 GCZGCShenandoah GC
首次发布JDK 7JDK 11(实验性)JDK 8+(实验性)
正式稳定版JDK 7JDK 15JDK 11+(稳定)
停顿时间几十毫秒级<10ms(与堆大小无关)<10ms (99.9%)
最大堆大小几十GBTB级别(官方测试16TB)TB级别
是否分代传统:否,Generational:是
内存开销中等较高(着色指针)中等(Brooks指针)
CPU开销中等较高(并发处理)中等(需要三种屏障)
核心技术区域化着色指针+读屏障Brooks指针+读屏障
适用场景中等延迟、大内存应用超大内存、超低延迟应用大内存、低延迟应用

二、G1 GC:平衡吞吐量与延迟的主流选择

适用场景

  1. 企业级应用:如ERP、CRM系统等传统企业应用
  2. 中等规模大数据处理:内存在几十GB级别的应用
  3. 需要可预测GC停顿时间的应用:对延迟要求中等(几十毫秒级)
  4. 混合工作负载:同时处理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级内存管理

适用场景

  1. 实时数据分析平台:如实时事件处理、流数据处理
  2. 高性能服务器:高并发Web服务、API网关
  3. 在线交易系统:需要毫秒级响应的交易系统
  4. 金融交易系统:高频交易、实时报价系统
  5. 云原生应用:需要管理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:分代优化的低延迟选择

适用场景

  1. 大内存服务:需要管理TB级别堆内存的云原生应用
  2. 低延迟系统:金融交易、实时推荐系统
  3. 突发分配应用:内存分配模式不规则的应用
  4. 分代优化场景: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需要三种屏障)

五、如何选择最适合的垃圾回收器

选择决策树

  1. 应用对延迟的要求是什么?

    • 如果需要<10ms的停顿,优先考虑ZGC或Shenandoah
    • 如果需要几十毫秒的停顿,G1是良好选择
  2. 应用需要处理的内存大小是多少?

    • 小于10GB:G1或ZGC
    • 10GB-100GB:G1或ZGC
    • 100GB+:ZGC或Shenandoah
  3. 应用的内存分配模式如何?

    • 规则分配:G1或ZGC
    • 不规则分配:Shenandoah(特别是Generational Shenandoah)
  4. CPU资源是否有限制?

    • CPU敏感环境:G1或Shenandoah(ZGC并发处理CPU开销较高)
    • CPU充足:ZGC或Shenandoah

实用选择指南

应用类型推荐GC原因
传统企业应用(ERP/CRM)G1 GC稳定性高,平衡吞吐量与延迟
中等规模大数据处理G1 GC可预测的停顿时间,内存在几十GB级别
实时数据分析平台ZGC低延迟、TB级内存支持
高性能Web服务ZGC高并发场景下低停顿
金融交易系统ZGC<10ms停顿,确保交易流畅
云原生微服务ZGC或ShenandoahTB级内存管理能力
突发内存分配应用Shenandoah GC适合不规则内存分配模式
分代优化需求应用Generational ShenandoahJDK 22+支持,吞吐量优势

六、总结与建议

  1. ZGC是当前超低延迟场景的最佳选择,尤其适合TB级内存应用和对延迟要求极高的场景。如果你的应用需要<10ms的停顿时间,且内存需求在TB级别,ZGC是首选。

  2. Shenandoah GC在分代优化后(JDK 22+)将成为ZGC的有力竞争者,特别是对于需要分代收集的低延迟应用。Generational Shenandoah有望在吞吐量上取得优势。

  3. G1 GC仍然是大多数应用的稳定选择,尤其适合中等规模、延迟要求不极端的应用。它经过长期验证,稳定性高,是大多数Java应用的默认选择。

  4. 在实际选择时,务必进行性能基准测试:不要仅依赖理论数据,应根据你的具体应用进行压力测试和GC日志分析。

  5. ZGC和Shenandoah都支持JDK 11+,建议升级到最新JDK版本以获得最佳性能。

小贴士:如果你正在从G1迁移到ZGC或Shenandoah,建议先在测试环境中进行性能基准测试,因为不同GC对应用性能的影响可能很大。ZGC在TB级内存上表现优异,但可能会增加CPU使用率;Shenandoah在分代优化后,吞吐量可能优于ZGC。