JVM 真的需要调优吗?你了解多少?

📑 文章目录

前言:面试官的 “灵魂拷问” 套路

面试官:工作中做过 JVM 调优吗?说说你都调过啥?

我内心:咱项目 QPS 连 10 都摸不到,上次问缓存穿透雪崩,这次又整 JVM 调优,真就往死里为难人呗。

别怕!我给大家整理了一套超全的 JVM 调优干货,面试能吹、实战能用,直接一套带走。


一、JVM 究竟需不需要调优?

JVM 发展这么多年,早就被打磨得很稳了。我个人觉得,99% 的场景其实根本用不着手动调优

平时我们配 JVM 参数,基本还是跟着官方推荐来就够用了:

这些默认参数,都是 JVM 团队经过大量测试、业界无数实践验证过的,正常用基本不会出什么大问题。

更何况,绝大多数项目 QPS 连 10 都不到,数据量也就几万级别,在这种低压力场景下,JVM 想出故障都难。

大家平时碰到的** OOM、CPU 飙高、GC 频繁**,多半都是代码问题导致的,改改代码就能解决,一般真没必要去动 JVM 参数。

连锁出现:GC 频繁 → CPU 高 → 最终 OOM

  • 静态集合持有大量对象(最常见!)
// 静态List永远不会被GC,一直往里面塞数据,必OOM
private static List<Object> cacheList = new ArrayList<>();

public void addData(Object data) {     
    cacheList.add(data);
}

原因:静态变量属于类生命周期,只要类不卸载,集合就不会被回收,数据无限堆积。

场景:本地缓存、临时数据、日志、请求参数无限制存储。

解决:加大小限制、使用 LRU 缓存、用完清空、用弱引用。

  • 无限递归(栈溢出 / 堆溢出)

结果:StackOverflowError 或 OOM。

原因:方法栈帧无限创建,耗尽栈 / 堆内存。

  • 大对象 + 无限创建(一次性加载全表数据)
// 一次性查100万条数据到内存
List<User> list = userMapper.selectAll();

解决:分页、流式查询、分批处理。

  1. CPU 高:查死循环、正则、GC、序列化、密集计算
  2. GC 频繁:查循环 new 对象、大对象、内存泄漏(程序用不到的对象,却一直占着内存不释放,GC 想回收也收不掉,时间久了就会 OOM)
  3. OOM:查静态集合、全表查询、ThreadLocal、无界队列、未关闭资源 总结:
  4. OOM 核心:对象无法回收、无限堆积
  5. CPU 高 核心:死循环、GC 疯狂、密集计算
  6. GC 频繁 核心:临时对象过多、内存泄漏
  7. 三者常常互相触发,优先排查 GC 日志


二、升级垃圾回收器就能解决问题?

网上有种说法:JVM 调优没必要,直接升级垃圾回收器就行。这个观点值得商榷。

实战角度

升级垃圾回收器确实是最有效的方式之一:

  • CMS → G1:性能提升明显,但 JDK8 中 G1 还存在不少问题
  • G1 → ZGC:最大暂停时间不超过 10ms,甚至 1ms

GC 做清理内存时,**必须暂时停掉你的业务代码,**这个 “停掉业务” 的时间,就叫 STW(Stop The World)

  • 停 100ms → 用户明显卡顿
  • 停 500ms → 接口超时、页面卡死
  • 停 1s → 系统雪崩

所以低延迟 GC 的核心目标:把 STW 压到极短,通常 1ms ~ 3ms

  • 标记、清理、转移 … 大部分工作和业务代码并发跑
  • STW 只做极少量根扫描,所以超快

G1 还要分段去做,停顿会随堆变大而变长。ZGC 停顿几乎不随堆大小变化

但 ZGC 并不是银弹,已知问题有:

  1. 吞吐量下降:相较 G1 最大下降 15%
  2. 高分配速率场景:唯一有效的"调优"方式就是增大堆内存

没有最好的,只有最合适的。

面试角度

如果你只回答"升级垃圾回收器",面试官大概率没听到他想要的答案。所以:

可以回答升级垃圾回收器,但不能只回答升级垃圾回收器。


三、JVM 何时优化?

《计算机程序设计艺术》作者高德纳说过:

过早的优化是编程中所有罪恶的根源。

忌过早优化,但不是完全不管。正确做法是:给核心服务的 JVM 指标配上监控告警,当指标出现波动时及时介入。

  • 新生代:放新创建的对象,回收频繁、速度快,对应 Young GC(YGC)
  • 老年代:放存活时间长的对象,回收少但慢,触发 Full GC(FGC)

对象如何从新生代 → 老年代

  1. 新对象先进入 Eden
  2. Eden 满 → YGC,存活对象进入 Survivor
  3. 在 Survivor 来回拷贝,年龄达标 晋升老年代
  4. 大对象 直接进入老年代
  5. 老年代满 → 触发 Full GC

JVM 核心指标参考范围

FGC = Full GC,全称 Full Garbage Collection,日常大家说的 FGC 飙高、FGC 频繁,就是指 Full GC 太多、太频繁

YGC / Young GC:只回收新生代

  • 速度快
  • STW 短
  • 频繁一点问题也不大

FGC / Full GC:回收整个堆

  • 新生代 + 老年代 + 元空间一起扫
  • 速度极慢
  • STW 很长,会直接导致服务卡顿、超时、雪崩

Grafana / Prometheus 里的指标:

  • jvm_gc_full_collections_total FGC 总次数
  • jvm_gc_full_collection_time_seconds FGC 总耗时

只要这几个指标正常,其他一般不会有问题。


四、JVM 优化步骤

分析和定位系统瓶颈

CPU 指标

JVM 内存指标

JVM GC 指标

这些都是临时文本日志,现在基本都用引入 actuator + micrometer,自动暴露 JVM 指标到 /actuator/prometheus

Prometheus 拉取指标,Grafana 导入 JVM 大盘(比如 4701、12856 等热门 dashboard)

最终你能在 Grafana 看到:

  • GC 次数、耗时
  • 堆内存趋势
  • 年轻代 / 老年代变化
  • 对象晋升
  • STW 时间
  • 元空间、直接内存

五、面试回答模板

当被问到 JVM 调优时,按以下逻辑回答:

  1. 表态:合理的 JVM 参数配置下,大多数情况不需要调优
  2. 补充:仍存在少量场景需要调优,应对核心指标配置监控告警
  3. 深挖:如果面试官追问分析方法,展示常用命令和工具

这一套流程下来,面试官对你的 JVM 功底会有不错的印象。


参考资料


一句话总结:JVM 调优不是常规操作,但掌握分析方法很重要。监控先行,定位瓶颈,量化目标,逐步优化。

本文链接: https://hyuzz-nuc.github.io/posts/jvm-tuning-guide/

未经作者禁止转载!