连载中 17/20

JVM 调优:先分清是谁的锅,再谈动参数

2026-09-19 · 5 阅读 · 0 评论 · 0 赞

调优漏斗:参数在最底层

遇到性能问题,从上往下过漏斗:架构层(缓存有没有、异步有没有、读写分离有没有)→ 代码层(循环里查库、无界集合、串行调用)→ JVM 参数。漏斗上层能解决九成问题——GC 频繁 80% 是"分配太多"而不是"回收太慢",把一次请求创建的临时对象砍半,比任何 GC 参数都立竿见影。调参是还完架构与代码的债之后才轮到的事。

目标先行:不可能三角

吞吐量、延迟、内存占用,三者互相牵制:堆越大单次 GC 越久但次数越少(吞吐↑停顿↑),并发收集器停顿短但总 CPU 开销大(延迟↑吞吐↓)。先定业务目标再选收集器:批处理要吞吐(Parallel),在线接口要 P99 停顿(G1/ZGC)。没有目标的调优就是乱拧旋钮,拧完也不知道是变好还是变坏。

一份务实的生产模板

-Xms4g -Xmx4g                    # 初始=最大,避免堆伸缩抖动
-XX:+UseG1GC                     # JDK 9+ 默认,显式写出防混淆
-XX:MaxGCPauseMillis=200         # 停顿目标,别设太狠(<100ms 伤吞吐)
-XX:MaxMetaspaceSize=512m        # 元空间上限,防动态类吃穿内存
-XX:+HeapDumpOnOutOfMemoryError  # OOM 自动留现场
-XX:HeapDumpPath=/data/dump/
-Xlog:gc*:file=/data/log/gc.log:time:filecount=10,filesize=50M
# 新生代大小交给 G1 自适应,手调 -Xmn 反而限制它的停顿模型

这份模板覆盖 80% 的常规服务。真正的调优是从它出发,看日志找具体瓶颈再动对应的旋钮——比如晋升过快调 Survivor/分代阈值,Humongous 频繁就调大 Region 尺寸,而不是背参数大全。

调优流程:一次只动一个旋钮

规范动作是:压测造流量 → 采集基线(GC 频率/停顿/吞吐)→ 单变量调整(一次只改一个参数)→ 同样压测对比 → 有效则保留、无效则回滚。多变量同调是调优大忌——改善了 A 指标却说不清是哪个参数的功劳,下次出问题也没有可复现的基线。这跟全链路压测的容量规划是一个思路:结论必须来自受控对比。

三个常见误区

抄大厂参数:人家 64G 堆的参数搬到你 4G 堆就是灾难,场景不同没有可比性。盲目加大堆:堆大了 Full GC 更疼——停顿时间随堆涨,4G 的 Full GC 秒级,32G 的分分钟能把服务判死刑;加大堆前先确认不是泄漏。只盯堆:元空间、直接内存、线程栈都在 -Xmx 之外,RSS 远超 -Xmx 是常态——容器里给 JVM 的内存配额要把这些算进去,否则容器 OOM Killer 杀进程,日志里连个 OOM 都没有。下一篇看 JVM 另一个隐身机制:JIT 是怎么悄悄改写你的代码的。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#JVM调优#G1参数#生产模板#压测对比#调优误区

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞