疏散失败本质是对象生命周期、晋升策略与内存布局错配,表现为长停顿、低Survivor使用率(<5%)、to-space exhausted等日志,主因包括MaxTenuringThreshold过高、SurvivorRatio过小、虚拟线程潮冲击及G1RegionSize不匹配。

疏散失败(Evacuation Failure)是 G1 GC 复制阶段中断的关键信号,本质不是“Survivor 不够大”,而是对象生命周期、晋升策略与内存布局三者错配的结果。它常伴随长停顿、低 Survivor 使用率(to-space exhausted 等日志线索,需结合参数逻辑与运行特征定位根因,再针对性拓宽年轻代承载边界。
从日志中识别真实疏散失败信号
它不总以“Evacuation Failure”明文出现:
- 显式标识:日志中含 [GC (Evacuation Failure)] 或 [G1 Evacuation Pause (young) (Evacuation Failure)]
- 隐性表现:[GC (Allocation Failure)] 后紧接超长 STW(>100ms),且 Survivor 使用率长期低于 5%,Eden 回收量大但晋升极少
- JDK 21+ 常见提示:to-space exhausted、evacuation failed、promotion failed(注意区分后者也可能源于 Survivor 容量不足)
四大底层驱动因素及对应日志特征
单纯调大 SurvivorRatio 往往无效,真正需要排查的是以下组合问题:
- MaxTenuringThreshold 过高(如=15):对象在 Survivor 区反复复制十几次才晋升,造成空间“占而不用”,碎片化严重;日志中可见 tenured 字段突增、Survivor occupancy 波动剧烈
- SurvivorRatio 设置过小(如=8):新生代固定时,Survivor 实际容量仅占 1/10,一次 Minor GC 就可能溢出;日志中 Survivor 区使用率频繁打满或直接报 desired survivor size overflow
- 虚拟线程潮冲击(如 Spring Boot 3.2 + virtual thread):Continuation/StackChunk 对象高频创建,Eden 快速填满,Survivor 来不及消化;日志中 YGC 频次陡增、单次耗时短但密度高,老年代增长缓慢
- G1RegionSize 与主流对象大小不匹配:若大量 512KB–1MB 对象集中分配,易跨 Region,加剧复制时 to-space 的连续空间压力;日志中常伴 Region allocation failure 或 Humongous allocation 相关提示
扩充年轻代承载边界的实操路径
不是堆参数叠加,而是分层控制变量、验证反馈:
- 优先降低晋升阈值:设 -XX:MaxTenuringThreshold=6,释放 Survivor 区压力;观察 GC 日志中 tenured 字段是否平稳上升而非脉冲式跳变
- 显式扩大 Survivor 比例:用 -XX:SurvivorRatio=4(即 Eden 占新生代 1/2,两个 Survivor 共占 1/2),提升复制缓冲弹性;避免设为 2 以下,以防 Eden 过小引发更频繁 Minor GC
- 适配虚拟线程负载:启用 -XX:+UseVirtualThreads(JDK 21+),并配合增大新生代整体容量(如 -Xmn1g),缓解 Eden 短期压测压力
- 校准 RegionSize:若应用存在稳定中等对象(如缓存块、消息体),可尝试 -XX:G1HeapRegionSize=1M,减少跨 Region 复制开销;但需同步检查 humongous object 数量是否因此下降
辅助验证与避坑要点
用对日志和参数才能避免误调:
- 开启详细 GC 日志:-Xlog:gc*,gc+age=trace,gc+phases=debug(JDK 10+),重点关注 evacuation、to-space、survivor-age-table 等字段
- 禁用自适应策略做基线对比:-XX:-UseAdaptiveSizePolicy,防止 JVM 动态调整掩盖真实瓶颈
- 切勿盲目增大 -Xmn:若老年代已持续上涨,说明对象本该晋升,此时扩年轻代只会推迟 Full GC,加重 STW 风险
- TLAB 调优可协同进行:-XX:TLABSize=512k 减少线程间分配竞争,间接降低 Eden 局部碎片,缓解疏散压力

















