JVM 性能调优
JVM性能调优是Java应用性能优化的重要环节。通过合理配置JVM参数,可以显著提升应用的吞吐量和响应速度。本章将介绍JVM调优的基本原则、常用参数和调优策略。
为什么要进行JVM调优?
常见性能问题
- 内存不足:频繁GC,甚至OOM
- GC停顿过长:影响用户体验
- 吞吐量不足:系统处理能力受限
- 响应延迟高:请求处理时间长
调优目标
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,响应变慢
分析:
- 查看GC日志,发现老年代快速增长
- 使用jmap分析堆内存,发现大对象
解决:
# 增大堆内存
-Xmx4g -Xms4g
# 增大新生代,减少对象晋升
-Xmn1g
# 使用G1收集器
-XX:+UseG1GC
案例2:内存泄漏
现象:内存持续增长,最终OOM
分析:
- 使用jmap生成堆转储
- 使用MAT分析,发现某个集合持续增长
解决:
- 修复代码中的内存泄漏
- 使用WeakHashMap或设置缓存过期策略
案例3:响应延迟高
现象:接口响应时间不稳定,偶发延迟
分析:
- 查看GC日志,发现GC停顿时间长
- 使用G1收集器的Mixed GC
解决:
# 使用G1收集器
-XX:+UseG1GC
# 降低停顿时间目标
-XX:MaxGCPauseMillis=100
# 提前触发并发GC
-XX:InitiatingHeapOccupancyPercent=35
调优最佳实践
1. 先监控,后调优
不要盲目调优,先收集数据:
- 开启GC日志
- 使用监控工具收集数据
- 分析问题原因
- 针对性调优
2. 调优参数最小化
只设置必要的参数,避免过度调优:
# 最小配置
-Xmx2g -Xms2g -XX:+UseG1GC -Xlog:gc*:file=gc.log
3. 代码优化优先
JVM调优是最后的手段,代码优化更重要:
- 减少对象创建
- 使用对象池
- 优化算法
- 避免内存泄漏
4. 测试验证
调优后必须进行测试验证:
- 压力测试
- 基准测试
- 对比调优前后性能
小结
JVM性能调优是一个系统工程:
- 调优目标:吞吐量、延迟、内存占用
- 内存参数:堆大小、新生代、元空间
- 垃圾收集器选择:根据场景选择合适的收集器
- 分析工具:jstat、jmap、jstack、VisualVM、Arthas
- 调优策略:先监控、后调优、代码优化优先