ZGC(Z Garbage Collector)是 JDK 11 引入的一款低延迟垃圾回收器,主要目标是将 GC 停顿时间控制在 10ms 以内,并且能在超大堆内存(TB 级别)下保持可预测的低延迟表现。


1. ZGC 的特点

  • 低延迟:GC 暂停时间通常不超过 1~2ms,不随堆大小增长而增加。
  • 可扩展性强:支持从 数百 MB 到 16TB 的堆大小。
  • 并发回收:大多数垃圾回收工作与应用线程并发进行。
  • 着色指针(Colored Pointers):利用 64 位地址空间中的未使用位来存储对象的 GC 状态标记,从而减少额外的内存访问和同步开销。
  • Region 化堆:将堆划分为大小不一的 Region,类似 G1,但 Region 更灵活(动态大小,2MB–16GB)。

2. ZGC 的回收过程

ZGC 的垃圾回收分为几个主要阶段:

  1. 初始标记 (Pause)
    • 短暂停顿,标记 GC Roots 直接可达的对象。
  2. 并发标记 (Concurrent Marking)
    • 和应用线程并发,遍历对象图,标记可达对象。
  3. 并发重定位 (Concurrent Relocation)
    • 将存活对象从旧 Region 移动到新 Region,避免碎片化。
    • 使用 读屏障 (Load Barrier) 保证应用访问对象时能找到新位置。
  4. 并发清理 (Concurrent Reset / Remap)
    • 释放无用的 Region,整理元数据。

3. ZGC 的优势场景

  • 超大堆:例如 100GB ~ TB 级别内存的应用(如大数据、AI 训练平台)。
  • 低延迟系统:金融交易、广告投放、在线游戏、高并发 Web 服务。
  • 需要高吞吐和稳定响应的应用

4. 使用方式

在启动 Java 程序时添加参数即可启用 ZGC:

java -XX:+UseZGC -Xmx16g -Xms16g -jar app.jar

常见参数:

  • -XX:+UseZGC 启用 ZGC。
  • -Xmx-Xms 设置堆大小。
  • -XX:ZUncommitDelay=300 设置释放未使用内存的延迟(默认 5 分钟)。

5. 与其他 GC 的对比

GC 类型停顿时间吞吐量最大堆支持适用场景
Serial一般小堆单核、小应用
Parallel大堆批处理、吞吐优先
CMS一般几百 GB延迟敏感但对碎片化敏感
G1中低数 TB通用低延迟
ZGC极低16TB超大堆、低延迟应用
Shenandoah极低一般数 TBRed Hat 提供,低延迟场景

加上代码(启动参数 + 示例程序 + 监控/调试命令)

下面把常用的 ZGC 启动参数GC 日志配置示例、一个简单的 压力测试 Java 程序(用于观察 ZGC 行为)以及常用的 监控 / 调试命令 都贴出来,方便你直接复制运行并观察效果。


1. 启动 JVM 的常用示例

推荐(JDK9+ 的 unified logging):

# 在生产/测试机器上启动应用(示例:16GB 堆)
java -XX:+UseZGC \
     -Xms16g -Xmx16g \
     -XX:ZUncommitDelay=300 \
     -Xlog:gc*:file=./logs/gc.log:time,uptimemillis,level,tags \
     -jar your-app.jar

如果你使用的是较老风格的 GC 日志(非必需,仅供参考):

java -XX:+UseZGC -Xms16g -Xmx16g \
     -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps \
     -jar your-app.jar

参数说明(简短):

  • -XX:+UseZGC:启用 ZGC。
  • -Xms / -Xmx:最小/最大堆。
  • -XX:ZUncommitDelay=300:空闲 region 释放回操作系统的延迟(秒)。
  • -Xlog:gc*:file=...:time,uptimemillis,level,tags:统一 GC 日志到文件并记录时间戳和标签。

2. 简单的压力测试 Java 程序(用于观察 ZGC 行为)

这个程序会不断分配中等大小的 byte[],并周期性打印内存占用,方便你在 GC 日志/监控工具下观察 ZGC 的并发回收与停顿。

// 文件名: ZGCStress.java
public class ZGCStress {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Starting ZGC stress test");
        Runtime rt = Runtime.getRuntime();

        java.util.List<byte[]> list = new java.util.ArrayList<>();
        int iter = 0;
        while (true) {
            // 每轮分配 100 个 1MB 的对象(可调整)
            for (int i = 0; i < 100; i++) {
                list.add(new byte[1024 * 1024]); // 1MB
            }

            // 控制 list 大小,避免 OOM:保留最近 N 批
            if (list.size() > 5000) { // 大约 5GB
                // 丢弃前面的一些引用,让它们成为垃圾
                for (int i = 0; i < 1000; i++) {
                    list.set(i, null);
                }
                // 轻微整理
                java.util.Iterator<byte[]> it = list.iterator();
                java.util.List<byte[]> newList = new java.util.ArrayList<>();
                while (it.hasNext()) {
                    byte[] b = it.next();
                    if (b != null) newList.add(b);
                }
                list = newList;
            }

            // 每 5 秒打印一次内存状态
            if (++iter % 2 == 0) {
                long total = rt.totalMemory();
                long free = rt.freeMemory();
                long used = total - free;
                System.out.printf("[%s] Used: %.2f MB, Total: %.2f MB, Free: %.2f MB%n",
                    java.time.LocalTime.now(),
                    used / 1024.0 / 1024.0,
                    total / 1024.0 / 1024.0,
                    free / 1024.0 / 1024.0);
            }

            Thread.sleep(2500); // 每轮间隔
        }
    }
}

编译并运行(示例):

javac ZGCStress.java
# 使用 ZGC 启动(示例 8GB 堆)
java -XX:+UseZGC -Xms8g -Xmx8g -Xlog:gc*:file=gc.log:time,uptimemillis,level,tags ZGCStress

观察:运行若干分钟后,打开 gc.log,并用 jstat/jcmd 等工具查看堆/GC 活动。


3. 常用的 JVM 运行时监控与调试命令

假设你已经获得目标 JVM 的 PID(用 jps / ps 找到):

  • 列出 JVM 进程:
jps -l

  • 触发一次显式 GC(让 GC 立刻执行):
jcmd <pid> GC.run

  • 获取堆直方图(类实例统计):
jcmd <pid> GC.class_histogram

  • 查看堆摘要(类似 jmap -heap):
jcmd <pid> GC.heap_info
# 或
jmap -heap <pid>

  • 实时监控(每秒)GC/堆信息:
jstat -gc <pid> 1000

  • 使用 VisualVM / Java Mission Control(JMC)连接观察 ZGC 的行为、线程、堆、延迟分布图(推荐用于 GUI 可视化)。

4. 常见调优点与注意事项(代码/参数相关)

  • 日志级别:使用 -Xlog:gc* 保存详细日志以便离线分析;线上谨慎开启高频日志以免 I/O 影响性能。
  • 小堆 & ZGC:ZGC 优势在超大堆与低延迟场景。对于小堆(几百 MB)不一定是最优选择。
  • 保持最新 JDK:ZGC 在较新 JDK(11+, 17+, 19+)上逐步改进,生产时尽量使用你平台支持的稳定版本。
  • ZUncommitDelay:控制从 OS 释放未使用内存的延迟(单位秒),可以根据内存波动调整。

5. 示例:结合 Systemd 启动脚本(可选)

如果你要在系统服务中启动,示例 systemd unit(片段):

[Unit]
Description=MyApp with ZGC

[Service]
User=app
ExecStart=/usr/bin/java -XX:+UseZGC -Xms16g -Xmx16g -XX:ZUncommitDelay=300 -Xlog:gc*:file=/var/log/myapp/gc.log:time,uptimemillis,level,tags -jar /opt/myapp/app.jar
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target