JVM场景设计题
JVM 场景题更接近真实工作:给你一个线上现象,要求你判断可能原因、采集证据、制定恢复和修复方案。
# 1. 服务运行几小时后 OOM,如何排查
思路:
- 查看 OOM 错误类型。
- 确认是否生成 heap dump。
- 查看 GC 日志,判断 Full GC 后内存是否下降。
- 使用 MAT 分析 dump。
- 查看 Dominator Tree 和 GC Roots。
- 找出异常增长对象和持有者。
- 对比业务日志,确认触发路径。
常见原因:
- 缓存无上限。
- ThreadLocal 未清理。
- 队列堆积。
- 会话对象未释放。
- 静态集合持有引用。
# 2. 接口 P99 延迟突然升高,如何判断是否 GC 导致
需要对齐时间线:
- 接口延迟曲线。
- GC 日志时间点。
- STW 停顿时间。
- CPU 使用率。
- 请求量。
- 发布记录。
如果 P99 抖动时间和 GC 停顿高度重合,且停顿时间接近延迟尖刺,就可以判断 GC 有较大嫌疑。
下一步分析:
- 是 Young GC 还是 Full GC。
- 是对象分配过快还是老年代压力。
- 是否有大对象或缓存增长。
# 3. CPU 100%,线程栈显示大量 RUNNABLE,怎么办
步骤:
- 找高 CPU 线程。
- 转换 nid。
- 多次抓 jstack。
- 看高 CPU 线程是否固定在同一业务栈。
- 如果不明确,使用 JFR 或 async-profiler。
可能原因:
- 死循环。
- 正则回溯。
- JSON 大对象序列化。
- 加密压缩。
- GC 线程繁忙。
- 锁竞争自旋。
# 4. 容器总是 OOMKilled,但 JVM 没有 OOM 日志
说明进程可能被容器或操作系统直接杀掉,JVM 来不及抛出 OOM。
排查:
- 查看容器事件。
- 查看 RSS。
- 查看
-Xmx是否过大。 - 查看直接内存、线程数、元空间。
- 查看 native memory。
修复:
- 降低
-Xmx。 - 限制直接内存。
- 限制线程数。
- 调整容器 memory limit。
- 使用百分比堆参数。
# 5. 频繁 Full GC 但堆 dump 看不出明显泄漏
可能原因:
- 堆太小。
- 大对象频繁分配。
- 晋升过快。
- 元空间触发。
- 显式
System.gc()。 - GC 参数不合理。
- 瞬时流量导致存活对象激增。
需要结合:
- GC 触发原因。
- 回收前后内存。
- 对象分配速率。
- 业务流量。
- 类加载数量。
没有泄漏不代表没有内存压力。
# 6. 如何给一个新服务设置 JVM 参数
建议:
- 明确 JDK 版本和部署环境。
- 明确容器内存和 CPU。
- 从默认 GC 开始,现代服务通常使用 G1。
- 设置合理堆大小,给堆外留空间。
- 开启 GC 日志。
- 配置 OOM dump。
- 设置元空间和直接内存上限。
- 压测后根据指标调整。
不要直接复制其他服务参数。
# 7. 如何设计 JVM 可观测性
应覆盖:
- 堆使用。
- 非堆使用。
- GC 次数和停顿。
- 线程数。
- 类加载数量。
- 直接内存。
- 进程 RSS。
- CPU。
- JFR 或 profiling 能力。
- OOM dump 和 GC 日志归档。
报警要关注趋势和业务影响,而不只是单点阈值。
# 8. 阶段小结
JVM 场景题的核心是证据链。优秀答案会先分类问题,再说明采集什么信息,如何判断,短期如何恢复,长期如何修复。不要一上来就背参数或重启服务。
# 9. 场景题通用答题模板
1. 先确认影响范围
哪些接口、哪些实例、是否核心链路
2. 保留现场
GC 日志、线程栈、堆 dump、JFR、业务日志
3. 分类判断
CPU / 内存 / GC / 线程 / IO / 容器
4. 短期止血
限流、摘实例、扩容、回滚、关闭任务
5. 根因定位
根据证据找到代码或配置问题
6. 长期修复
代码修复、参数调整、容量规划、监控补齐
# 10. 面试场景补充题
# 10.1 服务发布后 CPU 升高,但流量没变
分析方向:
- 新代码是否引入死循环或复杂计算。
- 是否新增日志拼接或 JSON 序列化。
- 是否依赖版本变化导致算法退化。
- 是否 JIT 预热不足。
- 是否 GC 频率上升。
证据:
- 发布前后 JFR 对比。
- 高 CPU 线程栈。
- GC 日志。
- 接口维度耗时。
# 10.2 老年代不高但频繁 Young GC
可能原因:
- 分配速率高。
- 新生代偏小。
- 请求中短命对象多。
- 日志、JSON、临时集合创建过多。
优化:
- 降低临时对象创建。
- 批量处理控制大小。
- 评估新生代大小。
- 用 JFR 看 allocation hotspot。
# 10.3 heap dump 显示缓存很大,但业务说缓存必要
回答重点:
- 缓存必要不代表可以无限增长。
- 要看命中率、最大容量、过期策略。
- 可按业务分层设置不同 TTL。
- 增加监控和降级策略。
# 专家速查表
| 场景题 | 回答主线 | 加分点 |
|---|---|---|
| CPU 飙高 | top 定位线程 + jstack | 十六进制 nid 对应 |
| 内存泄漏 | heap dump + GC 日志 | 支配树和引用链 |
| Full GC | GC cause + 对象分布 | 分配速率和晋升 |
| 启动慢 | 类加载、JIT、依赖初始化 | CDS、懒加载、预热 |
| 容器 OOM | 堆 + 堆外 + 线程栈 | cgroup 限制和 RSS |
场景题最忌只背参数。资深回答要先保存现场,再提出假设,用工具验证,最后给止血、根因修复和防复发方案。
# 11. Tips 快问快答
Q:场景题最忌讳什么?
A:一上来就说“调大堆”或“重启”,但没有证据和后续修复。
Q:如何体现资深度?
A:不仅能定位,还能考虑止血、风险、回滚、监控和复盘。
Q:调优题要不要给具体参数值?
A:可以给起点,但要说明必须结合压测和监控调整,不能当万能值。
Q:场景题回答多长合适?
A:先给主流程,再按面试官追问深入。不要一开始铺太散。
上次更新: 2026/06/25, 14:19:18