
为什么 G1 的 -XX:+UseG1GC 开了还是频繁 STW?
默认开启 G1 并不等于低停顿,G1 会根据堆大小、对象分配速率和 MaxGCPauseMillis 目标动态调整回收策略——但这个目标只是“软目标”,JVM 不保证达成。尤其在大堆(>8GB)、高分配率(如每秒数百 MB 新生对象)或混合回收不及时时,G1 会退化为 Full GC 或延长 Young GC 停顿。
-
-XX:MaxGCPauseMillis=200是关键起点,但设太低(如 50)会导致回收过于频繁、吞吐下降;设太高(如 500)则失去 G1 意义 - 必须配
-Xms和-Xmx相等,否则 G1 无法稳定预测区域大小,容易触发并发模式失败(Concurrent mode failure) - 避免混用
-XX:+UseStringDeduplication(尤其 JDK 8u20+),它在 GC 过程中加锁扫描字符串,显著拖长 STW
哪些参数真正影响 Young GC 停顿时间?
Young GC 停顿主要由存活对象复制量和卡表(card table)扫描开销决定,不是单纯调大堆就能缓解。
-
-XX:G1NewSizePercent=20和-XX:G1MaxNewSizePercent=40控制新生代弹性范围——值过小(如 5)会让 G1 频繁收缩/扩张,引发额外开销;过大(如 60)则挤压老年代并发标记空间,增加 Mixed GC 压力 -
-XX:G1HeapRegionSize必须是 2 的幂(1MB~4MB),且需整除堆大小;设错会导致区域数量异常,影响并行标记效率(常见错误:堆 16GB 却设G1HeapRegionSize=3MB) - JDK 9+ 可加
-XX:+G1UseAdaptiveIHOP让 JVM 自动调优初始堆占用阈值(IHOP),比硬编码-XX:G1InitiatingOccupancyPercent更稳
怎么判断是不是 Mixed GC 拖累了整体延迟?
Mixed GC 本该并发标记 + 少量老年代区域回收,但如果日志里出现 [GC pause (G1 Evacuation Pause) (mixed) 后紧跟着长时间 STW,大概率是回收区域太多或疏散失败。
- 用
-Xlog:gc*,gc+phases=debug(JDK 10+)或-XX:+PrintGCDetails -XX:+PrintGCTimeStamps(JDK 8)看每次 Mixed GC 实际回收的老年代区域数(regions: X)——超过 20% 总区域数就危险 -
-XX:G1MixedGCCountTarget=8可拆分回收压力,但不能低于 4,否则单次工作量反弹 - 若 Mixed GC 频繁失败(日志含
to-space exhausted),说明晋升太快,需检查-XX:G1HeapWastePercent(默认 5)是否太低,或加-XX:G1OldCSetRegionThresholdPercent=5限制单次回收老区数量
并发标记阶段卡住或超时怎么办?
并发标记(Concurrent Marking)本身不 STW,但它一旦失败(如被 Young GC 中断太多次),就会触发退化——这是隐蔽的 STW 源头。
立即学习“Java免费学习笔记(深入)”;
- 确保
-XX:ConcGCThreads足够(建议设为ParallelGCThreads / 4,最低 1),线程太少会导致标记跟不上分配速度 - 避免在标记中段触发大量 Young GC:
-XX:G1MixedGCLiveThresholdPercent=85(默认 85)可提高回收门槛,减少无效 Mixed GC - JDK 8u292+ 有
-XX:+G1EnableStringDeduplication替代旧版 dedup,但仅限确实存在大量重复字符串的场景,否则纯增开销
真正难调的是对象生命周期分布和跨代引用——参数再细,也盖不住业务层持续创建长生命周期对象又突然大批释放。这时候得看 SurvivorRatio 和 -XX:MaxTenuringThreshold 是否合理,而不是只盯着 G1 特有参数。










