面试官问:“线上Full GC频繁,你怎么排查?”你支支吾吾说“重启试试”。工作三年,写过业务、搭过框架,却连JVM调优都不会,这大概是很多Java程序员的真实写照。不是调优有多难,而是你一直没逼自己直面它。
为什么三年经验必须懂JVM调优
三年,意味着你不再只是写增删改查。线上OOM、CPU飙高、接口毛刺、服务假死,这些迟早会找上你。不懂JVM,你只能重启、加内存、换机器,治标不治本。面试官问JVM,不是要你背参数,而是看你有没有独立定位和解决线上问题的能力。
先搞懂内存结构和GC基础
JVM内存主要分堆、虚拟机栈、本地方法栈、方法区、程序计数器。调优主战场是堆:新生代(Eden+Survivor)和老年代。对象优先在Eden分配,Minor GC后存活对象进入Survivor,年龄够了晋升老年代。老年代满了触发Full GC,停顿长,影响大。常见收集器:Serial、Parallel、CMS、G1、ZGC。低延迟选G1或ZGC,高吞吐选Parallel。
调优的核心目标:先问业务要什么
调优不是让GC次数越少越好,而是平衡延迟和吞吐。Web接口要求低延迟,就控制GC停顿;离线计算追求吞吐,可以容忍长停顿。目标不同,参数策略完全不同。
必会的常用参数
-Xms和-Xmx设成一样,避免堆动态伸缩;-Xmn或-XX:NewRatio控制新生代;-XX:MetaspaceSize防元空间频繁扩容;-XX:+UseG1GC启用G1;-XX:MaxGCPauseMillis设期望停顿;-XX:+HeapDumpOnOutOfMemoryError留现场。别死记,要理解每个参数影响什么。
调优四步法:监控→分析→调整→验证
第一步监控:jstat -gcutil看GC频率和耗时;jmap -heap看内存分布;开启GC日志,用GCViewer或MAT分析。第二步分析:是Young GC频繁,还是Full GC频繁?是内存泄漏,还是堆太小?第三步调整:改参数或修代码。第四步验证:压测对比,确认指标改善。
三个高频场景与解法
场景一:频繁Full GC。先看老年代增长曲线,若持续上涨不下降,大概率内存泄漏。用jmap -dump导出堆,MAT找大对象和GC Roots。若曲线锯齿状,可能是堆太小或晋升过早,调大老年代或增大Survivor。
场景二:接口毛刺。通常是GC停顿导致。看GC日志,若单次停顿几百毫秒,换G1并设MaxGCPauseMillis=50,或调低新生代大小,减少单次回收范围。
场景三:OOM。堆溢出加-Xmx并查泄漏;元空间溢出调大MetaspaceSize;直接内存溢出查NIO或Netty的ByteBuf释放。
工具是手的延伸
jstat看实时GC,jmap导堆,jstack查线程,arthas在线诊断,MAT分析堆转储。别只会在IDEA里点运行,生产环境这些才是救命稻草。
总结
三年经验不会JVM调优,不丢人,丢人的是明知不会还不学。调优不是背几十个参数,而是理解内存模型、GC原理,用数据驱动决策。下次线上再卡,别急着重启,先看GC日志、导堆、定位根因。当你能说出“Full GC频繁是因为老年代增长过快,我加了-XX:+HeapDumpOnOutOfMemoryError并查到是缓存未设过期”,你就已经超过大多数三年经验的人了。