Java面试要点:Full GC频繁、服务卡顿与JDK 8、17、21 GC调优
前言
Full GC 频繁会导致接口抖动、服务卡顿甚至雪崩。面试中常见问题是:怎么判断是 GC 导致接口慢?Full GC 频繁怎么排查?JDK 8、17、21 的 GC 调优有什么区别?
Full GC 的影响
Full GC 通常会带来 Stop The World,应用线程暂停。表现为:
- 接口 P99 周期性升高。
- 监控出现请求尖刺。
- 日志时间戳中间出现明显空洞。
- CPU 升高但吞吐下降。
- 老年代回收后很快又涨满。
排查流程
flowchart TD
A[接口卡顿] --> B[查看GC监控]
B --> C[确认Full GC频率和耗时]
C --> D[分析GC日志]
D --> E[老年代是否持续增长]
E -->|是| F[堆Dump分析泄漏]
E -->|否| G[对象晋升/大对象/参数不合理]
G --> H[调整代码或JVM参数]
F --> H
GC 日志怎么看
重点关注:
- Young GC 次数和耗时。
- Full GC 次数和耗时。
- GC 前后堆使用量。
- 老年代回收效果。
- 晋升失败、分配失败。
- Humongous Object。
- Metaspace 使用量。
如果 Full GC 后老年代仍然很高,说明存活对象多,要怀疑内存泄漏或缓存过大。如果 Full GC 后能降下来,但很快又涨上去,说明对象分配速率过高或晋升过快。
JDK 8、17、21 的差异
| 版本 | 默认或常见 GC | 排查重点 |
|---|---|---|
| JDK 8 | Parallel GC,常用 CMS/G1 | CMS 碎片、晋升失败、永久代已变元空间 |
| JDK 17 | G1 常用 | Region、Mixed GC、Humongous Object |
| JDK 21 | G1 默认,ZGC 更成熟 | 低延迟场景可评估 ZGC,关注内存占用和吞吐权衡 |
JDK 17 和 21 更推荐使用统一日志参数 -Xlog:gc*,GC 日志格式和 JDK 8 不同。面试时不要把 JDK 8 的 CMS 经验直接套到 JDK 17/21。
常见根因
老年代持续增长
常见原因:
- 本地缓存无上限。
- 静态集合持有对象。
- ThreadLocal 未清理。
- 监听器、回调、定时任务持有对象。
- 大批量任务一次性加载数据。
处理方式是分析堆 Dump,而不是盲目加内存。
对象晋升过快
如果新生代太小,或者对象存活时间略长,就会快速晋升到老年代。
常见场景:
- 批量导入。
- 报表生成。
- 大列表查询。
- MQ 批量消费。
优化方式:
- 控制批量大小。
- 流式处理。
- 减少临时对象。
- 调整新生代比例或 G1 目标停顿时间。
大对象分配
大对象容易直接进入老年代或 G1 Humongous 区域。
常见对象:
- 大数组。
- 大字符串。
- 大 JSON。
- 大 Excel。
- 大图片或文件字节数组。
处理方式是避免一次性加载,改成流式、分片或临时文件。
GC 参数不合理
常见问题:
- 堆设置过小,导致频繁 GC。
- 堆设置过大,单次 GC 停顿过长。
- 容器内存限制与 JVM 参数不匹配。
- JDK 升级后沿用旧 GC 参数。
生产止血
- 临时扩容实例,降低单实例流量。
- 降低批处理并发和批量大小。
- 关闭异常报表、导出、补偿任务。
- 清理异常本地缓存。
- 对问题接口限流。
- 回滚最近发布。
如果是内存泄漏,扩容只能延迟故障再次发生,不能根治。
调优建议
- 先优化代码和对象生命周期,再调 JVM 参数。
- 建立 GC 基线,关注 Full GC 次数和 P99。
- 大批量任务不要和核心在线接口混部署。
- JDK 17/21 使用 G1 时重点看 Region、Mixed GC 和大对象。
- 对低延迟系统评估 ZGC,但要压测吞吐和内存成本。
高频面试题
Full GC 频繁怎么排查?
先看 GC 日志确认频率、耗时和回收效果。如果 Full GC 后老年代不下降,分析堆 Dump 找泄漏;如果能下降但很快上涨,排查对象分配速率、大对象和批处理。
JDK 8 和 JDK 17 的 GC 排查有什么不同?
JDK 8 常见 CMS/Parallel/G1,日志参数和问题类型不同。JDK 17 常用 G1,使用统一日志,重点关注 Region、Mixed GC、Humongous Object 和容器内存适配。
调大堆能解决 Full GC 吗?
只能缓解部分容量不足问题。如果是内存泄漏或大对象设计问题,调大堆会让单次 GC 更慢,故障仍会复现。
总结
Full GC 排查要看趋势和回收效果。面试时要讲清楚 GC 日志、堆 Dump、对象生命周期、JDK 版本差异和生产止血手段。


