G1 STW时延是否源于复制阶段,需看GC日志中“Evacuate Collection Set”耗时是否超整个pause的60%;Region大小需权衡:过大扫描空闲空间多,过小致RSet膨胀;碎片化会破坏停顿预测模型准确性,因对象地址不连续导致缓存命中率低、evacuation变慢。

怎么看 G1 的 STW 时延是否来自复制阶段
直接看 GC 日志里 G1 Evacuation Pause 的子项:如果 Evacuate Collection Set 耗时占整个 pause 的 60% 以上,基本可以判定复制(evacuation)是瓶颈。注意不是所有 STW 都是复制导致的——比如 Root Region Scan 或 Remembered Set Updating 也可能拉长时间,但它们不涉及对象移动。
常见误判点:
- 把
Mixed GC中老年代 Region 的并发标记阶段(Concurrent Marking)当成 STW —— 它实际不暂停应用线程 - 忽略 Humongous Region 清理触发的隐式 Full GC:
Humongous Allocation失败日志后紧跟着Full GC,这时 STW 是全局压缩,不是 G1 的复制逻辑 - 用
-XX:+PrintGCDetails但没加-XX:+PrintGCTimeStamps,导致无法对齐时间轴,看不出单次 pause 内各阶段耗时分布
Region 大小和碎片化程度如何影响 evacuation 耗时
-XX:G1HeapRegionSize 不是越大越好。Region 过大(如设为 32MB)会导致单个 Region 内对象分布稀疏,复制时要扫描大量空闲空间;过小(如 1MB)又会让 Remembered Set(RSet)膨胀,增加写屏障开销和 RSet 更新 STW 时间。
实测经验(堆 ≥ 16GB 场景):
- 中等活跃度服务(对象生命周期混合):用默认均分(约 2MB/Region)最稳
- 大量短生命周期小对象 + 偶发巨型缓存:设
-XX:G1HeapRegionSize=4M,能减少 Humongous Region 数量,避免因连续 H-Region 不足触发 Full GC - 堆内长期存在大量中等大小(2–8MB)对象:手动设
-XX:G1HeapRegionSize=8M,可降低 Region 总数,缓解 RSet 内存占用,但需配合-XX:G1RemSetTrackingRegionEntries调优
关键信号:GC log 中频繁出现 to-space exhausted,说明复制目标 Region 不够用,本质是碎片化已影响 evacuation 效率。
为什么停顿预测模型(Pause Prediction Model)在高碎片场景下会失效
-XX:MaxGCPauseMillis 是基于历史 evacuation 吞吐量做的滑动窗口预测,但它假设 Region 间存活对象密度相对均匀。一旦堆碎片化严重,G1 选出的回收集(Collection Set)里可能混入大量“低存活率但物理上分散”的 Region —— 看似垃圾多,实际要复制的对象地址不连续,CPU 缓存命中率暴跌,evacuation 速度远低于模型预期。
典型表现:
- 日志中
Target pause time: 200.0 ms,但实际 pause 经常飙到 400–600ms -
GarbageFirstCollector统计显示Avg GC time per region波动剧烈(标准差 > 均值 50%) - 同一轮 Mixed GC 中,部分 Region 的
Evacuation耗时是其他 Region 的 3–5 倍
此时强行调低 MaxGCPauseMillis 只会让 G1 更激进地选 Region,加剧不均衡,反而推高平均停顿。
真正能缓解碎片化复制延迟的操作项
别碰 -XX:+UseG1GC 开关本身 —— G1 就是为碎片化设计的。重点在三处:
- 关闭不必要的对象逃逸:用
-XX:+DoEscapeAnalysis(JDK8u20+ 默认开启),配合标量替换,减少堆分配,从源头减缓碎片生成 - 控制 Humongous Object:监控
G1 Humongous Allocations计数,对 >1MB 的 byte[]、String 等做池化或分块处理,避免触发 H-Region 连续分配失败 - 强制整理时机:当
Old Gen使用率长期 >70% 且Concurrent Cycle频繁失败时,用jcmd查 RSet 占用,若 RSet > 堆总内存 8%,说明碎片已导致元数据膨胀,需考虑VM.native_memory summary scale=MB -XX:G1MixedGCCountTarget=8加快老年代 Region 回收节奏
最易被忽略的一点:G1 的复制是“按 Region 搬运”,不是“按对象地址排序搬运”。所以哪怕堆逻辑上碎片化,只要 Region 内部对象局部性好,evacuation 依然快。优化对象创建模式(比如批量 new 同类对象)比调参数更治本。










