Java ZGC(Z Garbage Collector)从JDK 15转正以来,成为追求亚毫秒级停顿的GC首选方案。ZGC采用染色指针和读屏障实现并发标记-转移,停顿时间稳定在1ms以内且与堆大小无关。本文从ZGC核心机制、JDK参数配置、生产调优案例三个维度展开。
ZGC染色指针与并发转移原理解析
ZGC的核心创新是染色指针(Colored Pointers)。在64位指针的高4位嵌入标记位:Marked0、Marked1、Remapped、Finalizable。GC通过修改指针高位标记对象状态,业务线程通过读屏障(Load Barrier)在访问对象时自动修正指针。
// ZGC开启(JDK 17+)
// 启动参数
// java -XX:+UseZGC -Xmx32g -Xms32g -jar app.jar
// 关键JVM参数
// -XX:+UseZGC 启用ZGC
// -XX:+ZGenerational 启用分代ZGC(JDK 21+默认)
// -Xmx32g 最大堆大小
// -Xms32g 初始堆大小(生产建议与Xmx一致)
// -XX:SoftMaxHeapSize=24g 软上限,ZGC尽量保持堆在此值以下
// -XX:ZAllocationSpikeTolerance=2.0 分配尖峰容忍度
// -XX:ConcGCThreads=4 并发GC线程数
// -XX:ParallelGCThreads=16 STW阶段并行线程数
// -XX:+ZProactive 主动GC(避免被动触发)
// -XX:ZUncommitDelay=300 未使用内存回收延迟(秒)
分代ZGC(JDK 21+)将堆分为Young和Old两代,Young代使用复制算法高频回收,Old代并发标记整理低频回收。分代设计将扫描范围缩小到Young代,进一步降低GC停顿和CPU开销。
分代ZGC内存布局与分配缓冲区配置
ZGC堆内存划分为动态大小的ZPage(默认2MB),大对象分配专用Large ZPage。每个线程维护TLAB(Thread-Local Allocation Buffer)减少分配竞争。
// JDK 21 分代ZGC详细配置
// java -XX:+UseZGC -XX:+ZGenerational -Xmx64g -jar app.jar
// 关键参数说明:
// -XX:ZYoungGenerationSize=8g Young代固定大小
// -XX:MinTLABSize=4k TLAB最小大小
// -XX:TLABSize=64k TLAB默认大小
// -XX:-ResizeTLAB 禁止TLAB动态调整
// -XX:ZLargeAllocationThreshold=2M 大对象阈值
// -XX:ZCollectionInterval=120 强制GC间隔(秒)
// -XX:ZFragmentationLimit=25 碎片率阈值
// 代码中监控GC状态
import java.lang.management.ManagementFactory;
import java.lang.management.GarbageCollectorMXBean;
public class GCMonitor {
public static void main(String[] args) {
for (GarbageCollectorMXBean gc :
ManagementFactory.getGarbageCollectorMXBeans()) {
System.out.println("GC Name: " + gc.getName());
System.out.println("Collection Count: " + gc.getCollectionCount());
System.out.println("Collection Time: " +
gc.getCollectionTime() + "ms");
}
// 使用JFR持续监控GC停顿分布
// jcmd JFR.start name=zgc-monitor duration=60s \
// filename=/tmp/zgc.jfr settings=profile
}
}
生产环境调优案例:高并发交易系统
某交易系统32GB堆、QPS 5万、对象分配率8GB/s,从G1迁移到ZGC的调优过程。
// 调优前(G1配置)
// java -XX:+UseG1GC -Xmx32g -Xms32g \
// -XX:MaxGCPauseMillis=200 \
// -XX:G1HeapRegionSize=16m \
// -XX:InitiatingHeapOccupancyPercent=45 \
// -jar trading-app.jar
// 问题:Full GC偶发2-5秒停顿,P99延迟超标
// 调优后(ZGC分代配置)
// java -XX:+UseZGC -XX:+ZGenerational \
// -Xmx32g -Xms32g \
// -XX:SoftMaxHeapSize=26g \
// -XX:ConcGCThreads=8 \
// -XX:ParallelGCThreads=16 \
// -XX:+ZProactive \
// -XX:ZAllocationSpikeTolerance=3.0 \
// -Xlog:gc*:file=/var/log/app/zgc.log:time,level,tags \
// -XX:StartFlightRecording=filename=/var/log/app/zgc.jfr,\
// settings=profile,duration=600s \
// -jar trading-app.jar
// GC日志分析:ZGC日志格式
// [2026-08-15T10:00:01.123+0800] Garbage Collection (Warmup)
// Pause Mark Start 0.031ms
// [2026-08-15T10:00:01.456+0800] Garbage Collection (Warmup)
// Pause Mark End 0.024ms
// [2026-08-15T10:00:01.789+0800] Garbage Collection (Warmup)
// Pause Relocate Start 0.019ms
// JVM内嵌GC监控API(JDK 17+)
import jdk.jfr.consumer.RecordingStream;
public class ZGCMonitor {
public static void main(String[] args) {
try (var rs = new RecordingStream()) {
rs.onEvent("jdk.GarbageCollection", event -> {
System.out.printf(
"GC: %s, Pause: %.3fms, Heap: %dMB->%dMB%n",
event.getString("name"),
event.getDuration("duration").toMillis(),
event.getLong("heapUsedBefore") / 1024 / 1024,
event.getLong("heapUsedAfter") / 1024 / 1024
);
});
rs.startAsync();
}
}
}
ZGC内存映射与NUMA亲和性配置
ZGC使用多视图内存映射(Multi-View Mapping)实现指针染色,同一物理内存映射到多个虚拟地址空间。在多NUMA节点服务器上需正确配置CPU亲和性。
// NUMA感知配置(多路服务器)
// 确认NUMA拓扑
// numactl --hardware
// available: 2 nodes (0-1)
// node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// node 1 cpus: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// 使用numactl绑定JVM到指定NUMA节点
// numactl --cpunodebind=0 --membind=0,1 java -XX:+UseZGC -Xmx64g -jar app.jar
// 大页内存配置(降低TLB miss)
// echo 32768 > /proc/sys/vm/nr_hugepages # 分配64GB大页
// java -XX:+UseZGC -XX:+UseLargePages -XX:LargePageSizeInBytes=2m -jar app.jar
// 容器环境特殊配置
// Docker中运行ZGC需要禁用内存cgroup限制的OOM Killer干扰
// docker run --memory=64g --memory-swap=64g \
// -e JAVA_TOOL_OPTIONS="-XX:+UseZGC -XX:+ZGenerational -XX:SoftMaxHeapSize=56g" \
// myapp:latest
GC停顿诊断与内存泄漏排查
// 异常停顿诊断流程
// 1. 开启详细GC日志
// -Xlog:gc*=info,gc+phases=debug:file=zgc.log:time,level,tags
// 2. 使用JFR分析停顿分布
// jcmd JFR.start duration=300s filename=diagnostic.jfr settings=profile
// 3. 分析分配热点
// jfr print --events jdk.ObjectAllocationSample diagnostic.jfr | head -50
// 内存泄漏排查:ZGC堆dump
// jcmd GC.heap_dump /tmp/heapdump.hprof
// 使用MAT或JProfiler分析dump文件
// 代码层面避免分配热点
import java.util.concurrent.ConcurrentHashMap;
public class ObjectPool {
private static final ConcurrentHashMap POOL =
new ConcurrentHashMap<>();
public static StringBuilder acquire(long id) {
StringBuilder sb = POOL.computeIfAbsent(id, k -> new StringBuilder(1024));
sb.setLength(0);
return sb;
}
public static void processBatch(List orders) {
ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
for (Order order : orders) {
buffer.clear();
serializeOrder(order, buffer);
flushBuffer(buffer);
}
}
}
SoftMaxHeapSize是ZGC调优的关键参数,设置为Xmx的75-85%,ZGC会努力在软上限之内完成垃圾回收,避免堆膨胀到Xmx触发被动GC。ZAllocationSpikeTolerance设为2-5可应对突发分配尖峰,值越大预分配空间越充足但内存利用率降低。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/javazgc-la-ji-shou-ji-qi-di-yan-chi-diao-you-yu-nei-cun-can/