evacuation failure导致full gc的根因是对象生命周期与g1晋升策略错配,日志中“to-space overflow”或“evacuation failed”首发于young gc,引发早晋升→老年代碎片化→mixed gc跳过高碎片region→最终退化为serial式full gc。

面试中讲清楚 G1 因转移失败(Evacuation Failure)退化为 Full GC 的根因,关键不是罗列参数,而是用日志证据链还原“崩溃路径”:从一次 Young GC 的失败疏散,如何引发老年代碎片、Mixed GC 失效,最终触发单线程 Serial GC 式的全局停顿。下面分三步拆解,直击面试官想听的技术判断力。
第一步:锁定日志里的“转移失败”原始信号
不要只等 Full GC 日志出现才行动。真正的问题起点藏在 Young GC 日志里,必须一眼识别出以下任一明确表述:
- [GC pause (G1 Evacuation Pause) (young) (to-space overflow)] —— Survivor 区不够用,对象绕过幸存区直接晋升
- [Evacuation failed] —— 复制疏散阶段失败,G1 无法把存活对象搬进空闲 Region
- [Humongous allocation] —— 大对象分配失败,触发立即 Full GC 或强制退化
这三类日志不是孤立事件,而是同一压力源的不同表现。面试时可强调:“我不会只看 Full GC,而是倒查它前 2~3 次 Young GC 日志,找第一个 evacuation 失败点。”
第二步:关联失败后果——为什么一次失败会滚雪球?
转移失败不是“这次没清干净”,而是破坏了 G1 的回收节奏。需向面试官解释清楚传导逻辑:
- to-space overflow → 对象未经年龄筛选就进入老年代 → 老年代塞满大量短命对象 → Mixed GC 扫描时发现 Region 存活率虚高(如 95%),但实际是“假高”(因对象本不该在这)→ G1 认为不值得回收,跳过这些 Region
- Evacuation failed → G1 临时启用“Full GC fallback”机制 → 启动单线程 Serial GC 清理整个堆 → STW 时间飙升至秒级 → 接口超时、线程池打满、熔断触发
- Humongous allocation → 单个对象占满或跨多个 Region → 挤占可用 Region 数量 → 后续 Young GC 更易触发 to-space overflow → 形成恶性循环
第三步:给出可验证的定位与修复动作
面试官最看重你是否能闭环。不能只说“调参数”,而要说明每一步动作对应哪类日志证据:
- 查 Survivor 容量是否被锁死:检查 JVM 参数是否误配 -XX:SurvivorRatio 或 -XX:NewRatio —— G1 下这两个参数禁用,强行设置会导致 Survivor 区无法动态伸缩
-
验大对象是否高频出现:用 jcmd
VM.native_memory summary 或 GC 日志中的 Humongous 分配频次,结合业务确认是否有未分块的 JSON 反序列化、Base64 解码、缓存全量 dump 等场景 -
看老年代是否“越清越多”:用 jstat -gc
5000 每 5 秒采样,观察 OU(Old Used)是否阶梯式上升,而非平滑下降 —— 这是早晋升+内存泄漏的混合信号 - 改两个关键参数并验证效果:把 -XX:InitiatingHeapOccupancyPercent=35(提早启动并发标记),开启 -XX:G1ReclaimDeadHumongousObjectsAtYoungGC=true(在 Young GC 阶段顺手回收死亡大对象)
最后补一句收尾:“破除退化崩溃,本质是让 G1 的预测机制重新对齐真实对象生命周期。日志不是终点,而是对象行为在内存里的回声。”










