Appearance
ZGC(Z 垃圾回收器)
概述
ZGC(Z Garbage Collector)是 Oracle 开发的一款超低延迟垃圾回收器,设计目标是在任何堆大小下都能实现毫秒级的停顿时间。ZGC 采用了革命性的并发回收技术,包括并发标记、并发重定位和并发引用处理,几乎所有的垃圾回收工作都与应用线程并发执行。从 JDK 15 开始,ZGC 正式成为生产就绪的垃圾回收器。
基本特征
核心特点
- 超低延迟:停顿时间始终保持在 10ms 以下
- 可扩展性:支持 8MB 到 16TB 的堆内存
- 并发回收:几乎所有回收工作都与应用并发执行
- 内存效率:使用着色指针和负载屏障技术
- NUMA 感知:针对 NUMA 架构优化
适用场景
- 超大内存应用:堆内存 32GB 以上
- 极低延迟要求:金融交易、实时系统
- 云原生应用:容器化、微服务架构
- 大数据处理:需要处理大量数据的应用
核心技术
1. 着色指针(Colored Pointers)
ZGC 的核心创新是着色指针技术,将 GC 信息直接编码在指针中:
64 位着色指针布局(Linux x86_64):
┌─────────────────────────────────────────────────────────────┐
│ 63 62 61 60 59 58 57 56 55 ... 18 17 ... 0 │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ └─ 对象地址 │
│ │ │ │ │ │ │ │ │ └─────────── 未使用位 │
│ │ │ │ │ │ │ │ └─────────────── Finalizable │
│ │ │ │ │ │ │ └─────────────────── Remapped │
│ │ │ │ │ │ └─────────────────────── Marked1 │
│ │ │ │ │ └─────────────────────────── Marked0 │
│ │ │ │ └─────────────────────────────── 未使用位 │
│ │ │ └─────────────────────────────────── 未使用位 │
│ │ └─────────────────────────────────────── 未使用位 │
│ └─────────────────────────────────────────── 符号位 │
└─────────────────────────────────────────────────────────────┘
着色位含义:
- Marked0/Marked1:标记位,用于并发标记
- Remapped:重映射位,表示对象是否已重定位
- Finalizable:终结化位,表示对象是否需要终结化
优势:
- 无需额外的标记位图
- 支持并发标记和重定位
- 快速的对象状态检查
- 减少内存开销
2. 负载屏障(Load Barriers)
ZGC 使用负载屏障来处理并发访问和对象重定位:
java
// ZGC 负载屏障示例
public class ZGCLoadBarrier {
// 原始的对象访问
public Object getField(Object obj) {
return obj.field; // 编译器会插入负载屏障
}
// 负载屏障的概念实现
public Object loadBarrier(Object ref) {
// 1. 检查着色位
if (needsSlowPath(ref)) {
// 2. 进入慢路径处理
return slowPath(ref);
}
// 3. 快路径:直接返回
return ref;
}
private boolean needsSlowPath(Object ref) {
// 检查着色位是否匹配当前 GC 阶段
long colorBits = extractColorBits(ref);
long expectedColor = getCurrentGCPhaseColor();
return (colorBits & expectedColor) == 0;
}
private Object slowPath(Object ref) {
// 慢路径处理:
// 1. 标记对象(如果在标记阶段)
// 2. 重定位对象(如果在重定位阶段)
// 3. 更新引用
if (isMarkingPhase()) {
markObject(ref);
}
if (isRelocationPhase()) {
ref = relocateObject(ref);
}
return ref;
}
}
3. 区域管理(Region Management)
ZGC 将堆内存划分为不同大小的区域:
ZGC 区域大小分类:
1. 小区域(Small Regions):
- 大小:2MB
- 用途:存储小于 32KB 的对象
- 特点:高密度存储,减少碎片
2. 中等区域(Medium Regions):
- 大小:32MB
- 用途:存储 32KB - 16MB 的对象
- 特点:平衡存储密度和管理开销
3. 大区域(Large Regions):
- 大小:动态分配(2MB 的倍数)
- 用途:存储超过 16MB 的大对象
- 特点:每个对象独占一个或多个区域
区域状态:
- Empty:空闲区域
- Allocating:正在分配的区域
- Relocating:正在重定位的区域
- In-Use:正在使用的区域
工作流程
ZGC 的垃圾回收过程完全并发,包含以下主要阶段:
1. 并发标记(Concurrent Marking)
并发标记详细流程:
1. 根扫描(Root Scanning):
- 扫描 GC Roots(栈、全局变量等)
- 标记直接可达的对象
- 使用着色指针标记对象
- 停顿时间:< 1ms
2. 并发标记遍历(Concurrent Mark Traversal):
- 遍历对象图,标记所有可达对象
- 使用负载屏障处理并发修改
- 应用线程继续正常运行
- 标记信息存储在着色指针中
3. 标记完成(Mark Completion):
- 处理剩余的标记工作
- 统计存活对象信息
- 为重定位阶段做准备
- 停顿时间:< 1ms
标记算法特点:
- 使用三色标记算法
- 通过着色指针避免标记位图
- 支持增量标记
- 处理并发修改
2. 并发重定位(Concurrent Relocation)
并发重定位详细流程:
1. 重定位集选择(Relocation Set Selection):
- 选择需要回收的区域
- 优先选择垃圾最多的区域
- 考虑重定位成本和收益
- 停顿时间:< 1ms
2. 并发重定位(Concurrent Relocation):
- 将存活对象移动到新区域
- 更新对象的着色指针
- 建立转发表(Forwarding Table)
- 应用线程继续正常运行
3. 引用更新(Reference Update):
- 通过负载屏障延迟更新引用
- 在下次访问时更新引用
- 避免遍历整个堆
- 减少停顿时间
重定位特点:
- 完全并发执行
- 使用转发表跟踪对象移动
- 延迟引用更新
- 支持 NUMA 优化
3. 并发引用处理(Concurrent Reference Processing)
并发引用处理流程:
1. 弱引用处理:
- 处理 WeakReference 对象
- 清理不可达的弱引用
- 并发执行,不影响应用
2. 软引用处理:
- 根据内存压力处理 SoftReference
- 考虑对象的访问时间
- 平衡内存使用和性能
3. 虚引用处理:
- 处理 PhantomReference 对象
- 通知对象即将被回收
- 支持资源清理
4. 终结化处理:
- 处理需要 finalize 的对象
- 调用 finalize 方法
- 延迟对象回收
算法实现
ZGC 主要算法
java
// ZGC 垃圾回收器主要算法实现
public class ZGarbageCollector {
private final ZHeap heap;
private final ZBarrierSet barrierSet;
private final ZRelocationSet relocationSet;
private final ZForwardingTable forwardingTable;
// ZGC 回收的主要入口
public void collect() {
try {
// 1. 并发标记阶段
concurrentMark();
// 2. 并发重定位准备
prepareRelocation();
// 3. 并发重定位
concurrentRelocate();
// 4. 并发引用处理
concurrentReferenceProcessing();
} catch (Exception e) {
// 处理异常
handleGCException(e);
}
}
// 并发标记实现
private void concurrentMark() {
// 1. 根扫描(短暂停顿)
pauseAndScanRoots();
// 2. 并发标记遍历
concurrentMarkTraversal();
// 3. 标记完成(短暂停顿)
pauseAndFinishMarking();
}
private void pauseAndScanRoots() {
// 短暂停顿,扫描 GC Roots
stopAllApplicationThreads();
try {
// 扫描栈、全局变量等根对象
for (GCRoot root : getAllGCRoots()) {
Object obj = root.getObject();
if (obj != null) {
markObject(obj);
}
}
// 初始化标记队列
initializeMarkingQueues();
} finally {
resumeAllApplicationThreads();
}
}
private void concurrentMarkTraversal() {
// 并发标记遍历
while (hasUnprocessedObjects()) {
Object obj = getNextUnprocessedObject();
if (obj != null && isMarked(obj)) {
// 扫描对象的引用
scanObjectReferences(obj);
}
// 让出 CPU 时间
if (shouldYield()) {
Thread.yield();
}
}
}
private void scanObjectReferences(Object obj) {
// 扫描对象的所有引用字段
for (Object ref : obj.getReferences()) {
if (ref != null) {
// 通过负载屏障处理引用
ref = loadBarrier(ref);
// 标记引用的对象
if (!isMarked(ref)) {
markObject(ref);
}
}
}
}
// 并发重定位实现
private void concurrentRelocate() {
// 1. 选择重定位集合(短暂停顿)
pauseAndSelectRelocationSet();
// 2. 并发重定位对象
concurrentRelocateObjects();
}
private void pauseAndSelectRelocationSet() {
stopAllApplicationThreads();
try {
// 选择需要回收的区域
relocationSet.clear();
for (ZRegion region : heap.getAllRegions()) {
if (shouldRelocateRegion(region)) {
relocationSet.add(region);
}
}
// 初始化转发表
forwardingTable.initialize(relocationSet);
} finally {
resumeAllApplicationThreads();
}
}
private void concurrentRelocateObjects() {
// 并发重定位对象
for (ZRegion region : relocationSet.getRegions()) {
relocateRegion(region);
}
}
private void relocateRegion(ZRegion region) {
// 遍历区域中的所有存活对象
for (Object obj : region.getLiveObjects()) {
// 分配新的内存空间
Object newObj = heap.allocateObject(obj.getSize());
// 复制对象内容
copyObjectContent(obj, newObj);
// 更新转发表
forwardingTable.setForwarding(obj, newObj);
// 更新着色指针
updateColoredPointer(newObj);
}
// 释放原区域
heap.freeRegion(region);
}
}
负载屏障实现
java
// ZGC 负载屏障详细实现
public class ZLoadBarrier {
private final ZGCPhase currentPhase;
private final ZForwardingTable forwardingTable;
private final ZMarkingContext markingContext;
// 负载屏障主入口
public Object loadBarrier(Object ref) {
if (ref == null) {
return null;
}
// 快路径:检查着色位
if (isFastPath(ref)) {
return ref;
}
// 慢路径:需要特殊处理
return slowPath(ref);
}
private boolean isFastPath(Object ref) {
// 检查着色位是否匹配当前 GC 阶段
long colorBits = extractColorBits(ref);
long expectedColor = getExpectedColor();
return (colorBits & expectedColor) != 0;
}
private Object slowPath(Object ref) {
// 根据当前 GC 阶段处理
switch (currentPhase) {
case MARKING:
return markingSlowPath(ref);
case RELOCATION:
return relocationSlowPath(ref);
default:
return ref;
}
}
private Object markingSlowPath(Object ref) {
// 标记阶段的慢路径
if (!isMarked(ref)) {
// 标记对象
markObject(ref);
// 更新着色指针
ref = updateMarkingColor(ref);
}
return ref;
}
private Object relocationSlowPath(Object ref) {
// 重定位阶段的慢路径
Object forwardedRef = forwardingTable.getForwarding(ref);
if (forwardedRef != null) {
// 对象已被重定位
return forwardedRef;
}
// 检查对象是否在重定位集合中
if (isInRelocationSet(ref)) {
// 需要重定位对象
return relocateObject(ref);
}
// 更新着色指针
return updateRelocationColor(ref);
}
private Object relocateObject(Object ref) {
// 同步重定位对象
synchronized (ref) {
// 再次检查是否已被重定位
Object forwardedRef = forwardingTable.getForwarding(ref);
if (forwardedRef != null) {
return forwardedRef;
}
// 分配新内存
Object newRef = allocateNewObject(ref.getSize());
// 复制对象内容
copyObjectContent(ref, newRef);
// 更新转发表
forwardingTable.setForwarding(ref, newRef);
// 更新着色指针
newRef = updateRelocationColor(newRef);
return newRef;
}
}
// 着色指针操作
private long extractColorBits(Object ref) {
long address = getObjectAddress(ref);
return address & COLOR_MASK;
}
private Object updateMarkingColor(Object ref) {
long address = getObjectAddress(ref);
long newAddress = (address & ~COLOR_MASK) | MARKED_COLOR;
return getObjectFromAddress(newAddress);
}
private Object updateRelocationColor(Object ref) {
long address = getObjectAddress(ref);
long newAddress = (address & ~COLOR_MASK) | REMAPPED_COLOR;
return getObjectFromAddress(newAddress);
}
}
区域管理实现
java
// ZGC 区域管理实现
public class ZRegionManager {
private final List<ZRegion> smallRegions;
private final List<ZRegion> mediumRegions;
private final List<ZRegion> largeRegions;
private final ZRegionAllocator allocator;
public ZRegionManager(long heapSize) {
this.smallRegions = new ArrayList<>();
this.mediumRegions = new ArrayList<>();
this.largeRegions = new ArrayList<>();
this.allocator = new ZRegionAllocator(heapSize);
initializeRegions();
}
private void initializeRegions() {
// 初始化不同大小的区域
long totalSize = allocator.getTotalSize();
// 分配小区域(2MB)
int smallRegionCount = (int) (totalSize * 0.6 / SMALL_REGION_SIZE);
for (int i = 0; i < smallRegionCount; i++) {
ZRegion region = allocator.allocateRegion(SMALL_REGION_SIZE);
smallRegions.add(region);
}
// 分配中等区域(32MB)
int mediumRegionCount = (int) (totalSize * 0.3 / MEDIUM_REGION_SIZE);
for (int i = 0; i < mediumRegionCount; i++) {
ZRegion region = allocator.allocateRegion(MEDIUM_REGION_SIZE);
mediumRegions.add(region);
}
// 剩余空间用于大区域
long remainingSize = totalSize -
(smallRegionCount * SMALL_REGION_SIZE) -
(mediumRegionCount * MEDIUM_REGION_SIZE);
if (remainingSize > 0) {
ZRegion region = allocator.allocateRegion(remainingSize);
largeRegions.add(region);
}
}
public ZRegion allocateRegion(long objectSize) {
if (objectSize <= SMALL_OBJECT_THRESHOLD) {
return allocateSmallRegion();
} else if (objectSize <= MEDIUM_OBJECT_THRESHOLD) {
return allocateMediumRegion();
} else {
return allocateLargeRegion(objectSize);
}
}
private ZRegion allocateSmallRegion() {
for (ZRegion region : smallRegions) {
if (region.getState() == RegionState.EMPTY) {
region.setState(RegionState.ALLOCATING);
return region;
}
}
// 没有可用的小区域,尝试分配新区域
ZRegion newRegion = allocator.allocateRegion(SMALL_REGION_SIZE);
if (newRegion != null) {
smallRegions.add(newRegion);
newRegion.setState(RegionState.ALLOCATING);
return newRegion;
}
return null;
}
private ZRegion allocateMediumRegion() {
for (ZRegion region : mediumRegions) {
if (region.getState() == RegionState.EMPTY) {
region.setState(RegionState.ALLOCATING);
return region;
}
}
// 没有可用的中等区域,尝试分配新区域
ZRegion newRegion = allocator.allocateRegion(MEDIUM_REGION_SIZE);
if (newRegion != null) {
mediumRegions.add(newRegion);
newRegion.setState(RegionState.ALLOCATING);
return newRegion;
}
return null;
}
private ZRegion allocateLargeRegion(long objectSize) {
// 大对象需要专门的区域
long regionSize = alignToRegionSize(objectSize);
ZRegion newRegion = allocator.allocateRegion(regionSize);
if (newRegion != null) {
largeRegions.add(newRegion);
newRegion.setState(RegionState.ALLOCATING);
return newRegion;
}
return null;
}
public void freeRegion(ZRegion region) {
region.clear();
region.setState(RegionState.EMPTY);
// 可以考虑将区域返回给操作系统
if (shouldReturnToOS(region)) {
allocator.deallocateRegion(region);
removeRegionFromList(region);
}
}
private boolean shouldReturnToOS(ZRegion region) {
// 根据内存压力和区域使用情况决定
return allocator.getMemoryPressure() < LOW_MEMORY_THRESHOLD &&
region.getSize() >= LARGE_REGION_SIZE;
}
}
性能特征
优势
超低延迟:
- 停顿时间始终 < 10ms
- 与堆大小无关的停顿时间
- 适合延迟敏感应用
高可扩展性:
- 支持 8MB 到 16TB 堆内存
- 线性扩展性能
- NUMA 架构优化
并发性能:
- 几乎所有工作都并发执行
- 最小化应用线程停顿
- 高效的负载屏障
内存效率:
- 着色指针技术减少元数据
- 区域化管理减少碎片
- 支持内存压缩
劣势
内存开销:
- 着色指针需要 64 位平台
- 负载屏障有一定开销
- 转发表占用内存
吞吐量影响:
- 负载屏障影响应用性能
- 并发回收消耗 CPU 资源
- 可能低于 G1 GC 的吞吐量
平台限制:
- 仅支持 64 位平台
- 需要操作系统支持
- 某些平台特性可能不可用
JVM 参数配置
基础参数
bash
# 启用 ZGC
-XX:+UseZGC # 启用 ZGC 垃圾回收器
# 堆内存设置
-Xms32g # 初始堆大小
-Xmx128g # 最大堆大小
# ZGC 特定参数
-XX:SoftMaxHeapSize=120g # 软最大堆大小
ZGC 核心参数
bash
# 并发线程数
-XX:ConcGCThreads=12 # 并发 GC 线程数
# 内存管理
-XX:ZCollectionInterval=5 # GC 间隔时间(秒)
-XX:ZUncommitDelay=300 # 内存归还延迟(秒)
# 大对象处理
-XX:ZLargeObjectSizeThreshold=32m # 大对象阈值
高级参数
bash
# 性能调优
-XX:+UseLargePages # 使用大页内存
-XX:+UseTransparentHugePages # 使用透明大页
# NUMA 优化
-XX:+UseNUMA # 启用 NUMA 感知
# 调试参数
-XX:+PrintGC # 打印 GC 信息
-XX:+PrintGCDetails # 打印详细 GC 信息
-Xlog:gc*:gc.log # GC 日志输出
JDK 版本特定参数
bash
# JDK 11-14(实验性)
-XX:+UnlockExperimentalVMOptions
-XX:+UseZGC
# JDK 15+(生产就绪)
-XX:+UseZGC
# JDK 17+(增强特性)
-XX:+UseZGC
-XX:+ZGenerational # 分代 ZGC(实验性)
监控与调优
关键指标
延迟指标:
- 最大停顿时间
- 平均停顿时间
- 停顿时间分布
吞吐量指标:
- 应用吞吐量
- GC 开销占比
- 内存分配速率
内存指标:
- 堆内存使用率
- 区域分配情况
- 内存回收效率
调优策略
延迟优化
bash
# 减少停顿时间
-XX:ConcGCThreads=16 # 增加并发线程数
-XX:+UseLargePages # 使用大页减少 TLB 开销
-XX:ZCollectionInterval=1 # 更频繁的 GC
吞吐量优化
bash
# 提高吞吐量
-XX:ConcGCThreads=8 # 减少并发线程数
-XX:ZCollectionInterval=10 # 减少 GC 频率
-XX:SoftMaxHeapSize=90% # 设置软最大堆大小
内存优化
bash
# 内存使用优化
-XX:ZUncommitDelay=60 # 快速归还内存
-XX:+UseTransparentHugePages # 使用透明大页
-XX:ZLargeObjectSizeThreshold=16m # 调整大对象阈值
监控工具
JVM 内置工具
bash
# 查看 ZGC 统计
jstat -gc <pid> 1s
# 查看详细信息
jcmd <pid> GC.run
# 查看内存使用
jcmd <pid> VM.memory
# 查看 ZGC 特定信息
jcmd <pid> GC.run_finalization
GC 日志分析
bash
# 启用详细 ZGC 日志
-Xlog:gc*:gc.log:time,tags
# 关注的日志信息:
# - [gc] GC(N) Pause Mark Start
# - [gc] GC(N) Concurrent Mark
# - [gc] GC(N) Pause Mark End
# - [gc] GC(N) Concurrent Relocation
典型应用场景
1. 金融交易系统
bash
# 超低延迟交易系统配置
-XX:+UseZGC
-Xms64g -Xmx256g
-XX:SoftMaxHeapSize=240g
-XX:ConcGCThreads=16
-XX:+UseLargePages
-XX:+UseNUMA
-XX:ZCollectionInterval=1
2. 大数据处理
bash
# 大数据处理系统配置
-XX:+UseZGC
-Xms128g -Xmx512g
-XX:SoftMaxHeapSize=480g
-XX:ConcGCThreads=32
-XX:ZLargeObjectSizeThreshold=64m
-XX:+UseTransparentHugePages
3. 云原生应用
bash
# 容器化应用配置
-XX:+UseZGC
-Xms16g -Xmx64g
-XX:SoftMaxHeapSize=60g
-XX:ConcGCThreads=8
-XX:ZUncommitDelay=60
-XX:+UseContainerSupport
与其他 GC 对比
特性 | ZGC | G1 GC | CMS GC | Parallel GC |
---|---|---|---|---|
停顿时间 | < 10ms | 可控 | 短 | 长 |
吞吐量 | 中等 | 高 | 中等 | 最高 |
内存开销 | 高 | 中高 | 中等 | 低 |
堆大小支持 | 8MB-16TB | 4GB-64GB | 4GB-32GB | < 32GB |
并发程度 | 极高 | 部分 | 高 | 无 |
平台要求 | 64位 | 通用 | 通用 | 通用 |
成熟度 | 较新 | 成熟 | 成熟 | 成熟 |
复杂度 | 高 | 中等 | 高 | 简单 |
最佳实践
1. 选择标准
- 延迟要求:需要极低延迟(< 10ms)
- 堆内存大小:32GB 以上
- 平台支持:64 位 Linux/Windows
- 应用特性:延迟敏感的服务
2. 配置建议
- 合理设置堆大小:避免过小的堆内存
- 启用大页内存:减少 TLB 开销
- 调整并发线程数:根据 CPU 核数调整
- 监控内存使用:避免内存压力过大
3. 注意事项
- 评估吞吐量影响:ZGC 可能降低吞吐量
- 测试应用兼容性:验证应用在 ZGC 下的行为
- 监控系统资源:关注 CPU 和内存使用
- 定期性能测试:验证延迟和吞吐量指标
4. 迁移建议
- 从 G1 迁移:当延迟成为主要瓶颈时
- 从 CMS 迁移:当需要更好的可扩展性时
- 渐进式迁移:先在测试环境验证
故障排查
常见问题
吞吐量下降:
原因:负载屏障开销或并发 GC 消耗 CPU 解决: - 调整并发线程数 - 优化应用代码 - 考虑其他 GC 算法
内存使用过高:
原因:着色指针和转发表开销 解决: - 增加堆内存 - 调整 SoftMaxHeapSize - 优化对象生命周期
GC 频率过高:
原因:内存分配速率过快 解决: - 增加 ZCollectionInterval - 优化内存分配 - 调整堆大小
诊断工具
bash
# 分析 GC 日志
-Xlog:gc*:gc.log:time,tags
# 监控内存使用
jstat -gc <pid> 1s
# 分析堆转储
jmap -dump:format=b,file=heap.hprof <pid>
# 查看 ZGC 内部状态
jcmd <pid> GC.run_finalization
发展历程与未来
历史发展
- JDK 11:首次引入 ZGC(实验性)
- JDK 13:支持归还未使用内存给操作系统
- JDK 14:支持 macOS 和 Windows
- JDK 15:ZGC 正式生产就绪
- JDK 17:引入分代 ZGC(实验性)
未来发展
- 分代收集:支持分代垃圾回收
- 性能优化:继续优化吞吐量和延迟
- 平台扩展:支持更多平台和架构
- 易用性改进:简化配置和调优
总结
ZGC 是一个革命性的垃圾回收器,它通过着色指针和负载屏障技术实现了超低延迟的垃圾回收。虽然它在吞吐量方面可能不如传统的垃圾回收器,但对于延迟敏感的应用来说,ZGC 提供了无与伦比的性能保证。随着技术的不断发展和优化,ZGC 将成为大内存、低延迟应用的首选垃圾回收器。对于需要极致性能的现代应用,ZGC 代表了垃圾回收技术的未来方向。