4.JVM调优

#jvm #调优 #性能

1. 调优目标

核心目标:减少 Full GC 频率,缩短 GC 停顿时间

最常见的问题是内存分配不合理,对象频繁晋升老年代,导致频繁 Full GC,系统每隔几分钟卡顿几秒。分代模型和参数含义见 2.JVM内存划分,GC 触发条件见 3.垃圾回收,G 1 参数调优见 6.G1垃圾回收器

调优思路:让每次 Young GC 后的存活对象小于 Survivor 的 50%,尽量留在年轻代,减少进入老年代的对象。

2. 常用诊断工具

# 查看 GC 整体情况,每 1000ms 打印一次,共 10 次
jstat -gc <PID> 1000 10

# 年轻代 GC 分析(TT=存活时间,MTT=最大存活时间)
jstat -gcnew <PID>

# 老年代 GC 分析
jstat -gcold <PID>

# 查看堆内存分布
jmap -heap <PID>

# 按对象大小排序,找内存占用大的类
jmap -histo <PID>

# 导出内存快照
jmap -dump:live,format=b,file=dump.hprof <PID>

# 分析内存快照(浏览器访问 localhost:7000)
jhat dump.hprof -port 7000

jstat显示说明

线上推荐用 Arthas,比 jmap 更安全,不会触发 Full GC。更完整的调优参数参考 JVM性能调优方法

3. 压测观察指标

压测时重点关注:

4. 频繁 Full GC 的常见原因

原因 排查方式
Survivor 太小,对象频繁溢出到老年代 jstat -gcnew 看 Survivor 使用率
一次性加载大量数据(大对象) jmap -histo 看大对象
内存泄漏,对象无法被回收 MAT 分析 dump 文件
MetaSpace 不够,类加载过多 jstat -gcmetacapacity
代码中显式调用 System.gc() -XX:+DisableExplicitGC 禁止

5. 调优示例

问题:每次 Young GC 后有 20~30 MB 对象进入老年代,频繁触发 Full GC。

原因:Survivor 只有 10 MB,装不下存活对象,直接溢出到老年代。

调整前:

-XX:NewSize=100m -XX:MaxNewSize=100m
-XX:InitialHeapSize=200m -XX:MaxHeapSize=200m
-XX:SurvivorRatio=8   # Survivor 只有 10MB

调整后:扩大年轻代,调整 Survivor 比例:

-XX:NewSize=200m -XX:MaxNewSize=200m
-XX:InitialHeapSize=300m -XX:MaxHeapSize=300m
-XX:SurvivorRatio=2   # Survivor 扩大到 50MB

调优后GC详情

6. 新系统 JVM 参数设置流程

  1. 压测,收集 Eden 增长速率、Young GC 频率和耗时、存活对象大小
  2. 根据存活对象大小,确保 Survivor 能装下(存活对象 < Survivor 50%)
  3. 根据老年代增长速率,估算 Full GC 频率是否可接受
  4. 上线后持续监控,根据实际数据微调