调优实战

核心概念

JVM 调优的目标是在满足应用性能需求的前提下,合理分配和管理内存资源。调优不是盲目调参数,而是基于监控数据和问题分析的系统性工程。

JVM 参数配置

内存相关参数

# 堆内存
-Xms4g                          # 堆初始大小(建议与 -Xmx 相同,避免动态扩缩容)
-Xmx4g                          # 堆最大大小
-Xmn2g                          # 新生代大小
-XX:NewRatio=2                   # 老年代:新生代 = 2:1
-XX:SurvivorRatio=8              # Eden:S0:S1 = 8:1:1

# 栈内存
-Xss512k                        # 每个线程栈大小

# 元空间
-XX:MetaspaceSize=256m           # 元空间初始大小
-XX:MaxMetaspaceSize=512m        # 元空间最大大小

# 直接内存
-XX:MaxDirectMemorySize=1g       # 最大直接内存

GC 相关参数

生产环境推荐配置

监控与诊断工具

JDK 自带工具

工具
用途
常用命令

jps

查看 Java 进程

jps -lv

jstat

GC 统计信息

jstat -gc <pid> 1000

jmap

内存映射

jmap -heap <pid>

jstack

线程快照

jstack <pid>

jcmd

综合诊断

jcmd <pid> VM.flags

jinfo

JVM 参数

jinfo -flags <pid>

jstat — GC 监控

jmap — 堆分析

jstack — 线程分析

线程状态说明:

Arthas — 在线诊断神器

故障排查实战

场景一:CPU 飙高排查

常见原因:

  • 死循环

  • 频繁 GC(GC 线程占用 CPU)

  • 正则表达式回溯

  • 加密/序列化等计算密集型操作

场景二:内存泄漏排查

常见内存泄漏原因:

  • 集合类持有对象引用未释放

  • 静态集合不断增长

  • 连接/流未关闭

  • ThreadLocal 未 remove

  • 内部类持有外部类引用

  • 监听器/回调未注销

场景三:频繁 Full GC

常见原因和解决方案:

原因
分析方法
解决方案

老年代空间不足

jstat -gc 观察 OU 持续增长

增大堆内存或排查内存泄漏

元空间不足

MC/MU 接近上限

增大 MaxMetaspaceSize

大对象直接进入老年代

分析堆转储中大对象

调整 PretenureSizeThreshold

晋升失败

GC 日志显示 Promotion Failed

增大 Survivor 或老年代

System.gc() 调用

GC 日志显示 System.gc()

添加 -XX:+DisableExplicitGC

场景四:死锁排查

GC 调优方法论

调优目标

根据应用类型确定优先级:

应用类型
优先指标
推荐收集器

Web 服务

低延迟

G1 / ZGC

批处理

高吞吐

Parallel

交易系统

超低延迟

ZGC

微服务

均衡

G1

调优步骤

调优原则

  1. 不要过早优化:先保证功能正确,再考虑性能

  2. 基于数据调优:不要凭直觉调参数,用 GC 日志和监控数据说话

  3. 每次只改一个参数:方便对比效果

  4. Xms = Xmx:避免堆大小动态调整带来的开销

  5. 关注 Full GC:Full GC 频率和耗时是关键指标

面试要点

高频问题

  1. 生产环境 JVM 参数怎么配?

    • Xms=Xmx,避免动态扩缩容

    • 开启 GC 日志和 OOM 堆转储

    • 根据应用类型选择收集器

  2. 线上 CPU 100% 怎么排查?

    • top → top -Hp → printf hex → jstack grep

  3. 内存泄漏怎么排查?

    • jstat 观察趋势 → jmap 堆转储 → MAT 分析

  4. 频繁 Full GC 怎么处理?

    • 先看 GC 日志确认原因

    • 老年代不足、元空间不足、大对象、晋升失败

  5. Arthas 用过吗?常用哪些命令?

    • dashboard、thread、trace、watch、heapdump

常见陷阱

  • 不要盲目增大堆内存,可能导致 GC 停顿时间更长

  • -XX:+DisableExplicitGC 可能导致 NIO 直接内存无法及时回收

  • 生产环境不要随意使用 jmap -dump:live,会触发 Full GC

参考资料

Last updated