跳到主要内容

JVM 性能调优

JVM性能调优是Java应用性能优化的重要环节。通过合理配置JVM参数,可以显著提升应用的吞吐量和响应速度。本章将介绍JVM调优的基本原则、常用参数和调优策略。

为什么要进行JVM调优?

常见性能问题

  1. 内存不足:频繁GC,甚至OOM
  2. GC停顿过长:影响用户体验
  3. 吞吐量不足:系统处理能力受限
  4. 响应延迟高:请求处理时间长

调优目标

JVM内存参数

堆内存设置

# 设置初始堆大小
-Xms512m

# 设置最大堆大小
-Xmx2g

# 推荐:初始堆和最大堆设置为相同值,避免动态扩容
-Xms2g -Xmx2g

设置原则

  • 堆大小通常设置为物理内存的50%-80%
  • 预留足够内存给操作系统和其他进程
  • 初始堆和最大堆设置为相同值

新生代设置

# 设置新生代大小
-Xmn512m

# 设置新生代与老年代比例(新生代占比)
-XX:NewRatio=2 # 新生代:老年代 = 1:2

# 设置Eden与Survivor比例
-XX:SurvivorRatio=8 # Eden:Survivor = 8:1:1

设置原则

  • 新生代太小:频繁Minor GC
  • 新生代太大:老年代空间不足,Full GC频繁
  • 一般设置为堆大小的1/3到1/4

元空间设置

# 设置元空间初始大小
-XX:MetaspaceSize=128m

# 设置元空间最大大小
-XX:MaxMetaspaceSize=512m

直接内存设置

# 设置直接内存最大大小
-XX:MaxDirectMemorySize=256m

垃圾收集器参数

Serial收集器

# 使用Serial收集器
-XX:+UseSerialGC

适用于:客户端应用、小内存场景

Parallel收集器

# 使用Parallel收集器(JDK8默认)
-XX:+UseParallelGC

# 设置最大GC停顿时间目标
-XX:MaxGCPauseMillis=200

# 设置吞吐量目标(GC时间占比)
-XX:GCTimeRatio=99 # 1/(1+99) = 1% GC时间

# 启用自适应调节
-XX:+UseAdaptiveSizePolicy

适用于:批处理、后台计算、吞吐量优先

G1收集器

# 使用G1收集器(JDK9+默认)
-XX:+UseG1GC

# 设置最大GC停顿时间目标
-XX:MaxGCPauseMillis=200

# 设置Region大小(1-32MB,2的幂)
-XX:G1HeapRegionSize=4m

# 设置触发并发GC的堆占用阈值
-XX:InitiatingHeapOccupancyPercent=45

# 设置Mixed GC次数
-XX:G1MixedGCCountTarget=8

适用于:服务端应用、大内存、低延迟

CMS收集器(JDK14已移除)

# 使用CMS收集器
-XX:+UseConcMarkSweepGC

# 设置CMS启动阈值
-XX:CMSInitiatingOccupancyFraction=75

# 启用CMS压缩
-XX:+UseCMSCompactAtFullCollection

ZGC收集器

# 使用ZGC收集器(JDK15+)
-XX:+UseZGC

# 启用并发类卸载
-XX:+ZUncommit

# 设置最大GC停顿时间
-XX:MaxGCPauseMillis=10

适用于:大内存、超低延迟

GC日志参数

JDK8及之前

# 打印GC详细信息
-XX:+PrintGCDetails

# 打印GC时间戳
-XX:+PrintGCDateStamps

# 打印GC原因
-XX:+PrintGCCause

# 打印堆信息
-XX:+PrintHeapAtGC

# 输出GC日志到文件
-Xloggc:/path/to/gc.log

JDK9及之后

# 统一日志配置
-Xlog:gc*:file=/path/to/gc.log:time,level,tags

# 打印GC详细信息
-Xlog:gc*

# 打印GC原因
-Xlog:gc+cause

# 打印堆信息
-Xlog:gc+heap

常用调优策略

吞吐量优先

适用于批处理、后台计算任务:

java -Xmx4g -Xms4g \
-XX:+UseParallelGC \
-XX:NewRatio=2 \
-XX:SurvivorRatio=8 \
-XX:GCTimeRatio=99 \
-XX:+UseAdaptiveSizePolicy \
-jar app.jar

延迟优先

适用于Web服务、实时系统:

java -Xmx4g -Xms4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=100 \
-XX:InitiatingHeapOccupancyPercent=35 \
-XX:G1HeapRegionSize=8m \
-jar app.jar

大内存场景

适用于大数据处理、缓存服务:

java -Xmx16g -Xms16g \
-XX:+UseZGC \
-XX:MaxGCPauseMillis=10 \
-XX:ZCollectionInterval=5 \
-jar app.jar

容器环境

在Docker/Kubernetes中运行时:

# JDK8u191+自动识别容器内存限制
java -XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-XX:InitialRAMPercentage=75.0 \
-jar app.jar

性能分析工具

jstat

实时查看GC统计信息:

# 每秒输出GC统计
jstat -gc <pid> 1000

# 输出GC汇总
jstat -gcutil <pid>

# 输出GC原因
jstat -gccause <pid>

jmap

生成堆转储文件:

# 生成堆转储
jmap -dump:format=b,file=heap.hprof <pid>

# 查看堆配置
jmap -heap <pid>

# 查看对象统计
jmap -histo <pid> | head -20

jstack

查看线程堆栈:

# 打印线程堆栈
jstack <pid>

# 检测死锁
jstack -l <pid>

VisualVM

图形化监控工具,JDK自带:

# 启动VisualVM
jvisualvm

JConsole

JMX监控工具:

# 启动JConsole
jconsole

Arthas

阿里开源的Java诊断工具:

# 启动Arthas
java -jar arthas-boot.jar

# 查看Dashboard
dashboard

# 监控方法执行
watch com.example.Service method '{params, returnObj}'

# 追踪方法调用路径
trace com.example.Service method

调优案例分析

案例1:频繁Full GC

现象:应用频繁Full GC,响应变慢

分析

  1. 查看GC日志,发现老年代快速增长
  2. 使用jmap分析堆内存,发现大对象

解决

# 增大堆内存
-Xmx4g -Xms4g

# 增大新生代,减少对象晋升
-Xmn1g

# 使用G1收集器
-XX:+UseG1GC

案例2:内存泄漏

现象:内存持续增长,最终OOM

分析

  1. 使用jmap生成堆转储
  2. 使用MAT分析,发现某个集合持续增长

解决

  1. 修复代码中的内存泄漏
  2. 使用WeakHashMap或设置缓存过期策略

案例3:响应延迟高

现象:接口响应时间不稳定,偶发延迟

分析

  1. 查看GC日志,发现GC停顿时间长
  2. 使用G1收集器的Mixed GC

解决

# 使用G1收集器
-XX:+UseG1GC

# 降低停顿时间目标
-XX:MaxGCPauseMillis=100

# 提前触发并发GC
-XX:InitiatingHeapOccupancyPercent=35

调优最佳实践

1. 先监控,后调优

不要盲目调优,先收集数据:

  1. 开启GC日志
  2. 使用监控工具收集数据
  3. 分析问题原因
  4. 针对性调优

2. 调优参数最小化

只设置必要的参数,避免过度调优:

# 最小配置
-Xmx2g -Xms2g -XX:+UseG1GC -Xlog:gc*:file=gc.log

3. 代码优化优先

JVM调优是最后的手段,代码优化更重要:

  • 减少对象创建
  • 使用对象池
  • 优化算法
  • 避免内存泄漏

4. 测试验证

调优后必须进行测试验证:

  • 压力测试
  • 基准测试
  • 对比调优前后性能

小结

JVM性能调优是一个系统工程:

  1. 调优目标:吞吐量、延迟、内存占用
  2. 内存参数:堆大小、新生代、元空间
  3. 垃圾收集器选择:根据场景选择合适的收集器
  4. 分析工具:jstat、jmap、jstack、VisualVM、Arthas
  5. 调优策略:先监控、后调优、代码优化优先

参考资料