工作三年,写得了业务,改得了Bug,唯独听到“JVM调优”四个字就心里发虚。面试被问GC,只能背八股;线上CPU飙高,第一反应是重启;看到Full GC日志,像看天书。你以为是知识不够,其实是缺一套可落地的公式。
JVM调优不是玄学,它本质是一个“监控→分析→调整→验证”的闭环。我把它总结成一套万能公式:定目标、开日志、看指标、调参数、压测验证。照着做,80%的调优场景都能搞定。
第一步:定目标。调优前先问自己:我要吞吐量还是低延迟?Web应用通常追求低延迟,GC停顿不能超过200ms;批处理任务追求高吞吐,停顿长点无所谓。目标不同,策略完全不同。别一上来就调参数,那是耍流氓。
第二步:开日志。没有GC日志,调优就是瞎猜。Java 8用-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log,Java 11+用-Xlog:gc:file=gc.log。日志里藏着一切真相:Young GC频率、Full GC次数、每次停顿时间、堆内存变化。
第三步:看指标。用jstat -gcutil pid 1000实时观察。重点看三个数:Young GC频率(几秒一次算正常,几百毫秒一次说明新生代太小)、Full GC频率(一天几次可以忍,一小时几次必须查)、GC停顿时间(超过1秒就要警惕)。再看堆使用曲线,如果老年代持续上涨不下降,八成是内存泄漏。
第四步:调参数。记住几个核心参数:
堆大小:-Xms和-Xmx设成一样,避免动态扩容。通常设为物理内存的50%-70%。
新生代:-Xmn设为堆的1/3左右。太小导致Young GC频繁,太大导致Young GC停顿长。
GC器:低延迟选G1(-XX:+UseG1GC),大堆(>16G)选ZGC,吞吐优先选Parallel。
元空间:-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m,避免频繁Full GC。
G1关键参数:-XX:MaxGCPauseMillis=200设定目标停顿,-XX:InitiatingHeapOccupancyPercent=45触发并发标记。
第五步:压测验证。调完参数必须压测,对比调优前后的TPS、响应时间、GC频率。没有验证的调优,等于没调。
举个真实案例:一个Spring Boot服务,Full GC每10分钟一次,每次停顿2秒。按公式排查:开日志发现老年代增长快,用jmap -histo:live发现某个本地缓存没设过期,越积越多。修复代码后,调整-Xmx4g -Xmn1g -XX:+UseG1GC -XX:MaxGCPauseMillis=200,Full GC降到每天一次,停顿200ms以内。
这套公式的精髓不是死记参数,而是养成“先看后调、数据驱动”的思维。JVM调优没有银弹,但有章法。下次再遇到OOM或CPU飙高,别急着重启,按这个公式走一遍:开日志、看指标、找瓶颈、调参数、压测验证。三次之后,你会发现JVM调优不过如此。
工作三年还没搞懂,不是因为你笨,而是没人告诉你——调优不是背出来的,是算出来的。把这套公式拿去用,下次面试,你就是那个能说清楚“为什么这么调”的人。