JVM 调优实战指南
JVM 调优是提升 Java 应用性能的关键,需要理解 JVM 原理和掌握调优工具。
一、JVM 参数配置
1.1 堆内存参数
# 初始堆大小
-Xms2g
# 最大堆大小
-Xmx4g
# 新生代大小
-Xmn1g
# 老年代大小(JDK 8+)
-XX:NewSize=512m
-XX:MaxNewSize=1g
# 堆比例(新生代:老年代)
-XX:NewRatio=2 # 1:2
1.2 栈内存参数
# 线程栈大小
-Xss256k # 默认 1M,高并发可调小
# 元空间
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
1.3 GC 参数
# 使用 G1 收集器
-XX:+UseG1GC
# 最大停顿时间
-XX:MaxGCPauseMillis=200
# 并行 GC 线程数
-XX:ParallelGCThreads=8
# 并发 GC 线程数
-XX:ConcGCThreads=2
1.4 日志参数
# JDK 8
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/var/log/gc.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=10M
# JDK 9+
-Xlog:gc*:file=gc.log:time,uptime,level,tags
二、GC 日志分析
2.1 日志解读
# GC 日志示例
2024-01-15T10:30:00.123+0800:
[GC (Allocation Failure)
[PSYoungGen: 256000K->32000K(307200K)] # 新生代回收
512000K->288000K(1024000K), # 堆整体回收
0.0123456 secs] # 耗时
# 字段说明
// 回收前大小->回收后大小 (总容量)
// 堆回收前->堆回收后 (堆总容量)
2.2 关键指标
# 关注指标
- GC 频率:Minor GC 间隔
- GC 耗时:单次 GC 时间
- 晋升速率:新生代→老年代
- 堆使用率:GC 后堆占用
# 正常指标
- Minor GC:< 50ms,每秒 1-2 次
- Full GC:< 1 秒,数分钟一次
- 堆使用率:GC 后 < 50%
2.3 分析工具
# GCViewer(可视化)
java -jar gcviewer.jar gc.log
# GCEasy(在线)
# https://gceasy.io/
# 手动分析
grep "Full GC" gc.log | wc -l # Full GC 次数
grep "GC" gc.log | awk '{print $NF}' | sort -n | tail # 最慢 GC
三、问题排查
3.1 内存泄漏
# 症状
- Full GC 频繁
- 堆使用率持续上升
- Old 区只增不减
# 排查步骤
# 1. dump 堆
jmap -dump:format=b,file=heap.hprof <pid>
# 2. MAT 分析
# 打开 heap.hprof,查看 Dominator Tree
# 3. 定位泄漏源
# 查找 GC Roots,分析引用链
3.2 频繁 Full GC
# 原因
1. 老年代空间不足
2. 元空间不足
3. System.gc() 调用
4. 堆 dump
# 解决
# 1. 增大堆
-Xmx4g -Xms4g
# 2. 调整新生代
-Xmn2g
# 3. 禁用显式 GC
-XX:+DisableExplicitGC
# 4. 调整晋升阈值
-XX:MaxTenuringThreshold=10
3.3 OOM 问题
# 常见 OOM
1. Java heap space
2. Metaspace
3. GC overhead limit exceeded
4. Out of swap space
# 解决
# 1. 堆空间不足
-Xmx4g
# 2. 元空间不足
-XX:MaxMetaspaceSize=512m
# 3. GC 开销过大
# 优化代码,减少对象创建
# 4. 内存泄漏
# dump 分析
3.4 CPU 过高
# 排查步骤
# 1. 找到高 CPU 进程
top
# 2. 找到高 CPU 线程
top -Hp <pid>
# 3. 转换线程 ID
printf "%x\n" <tid>
# 4. 查看线程栈
jstack <pid> | grep <hex_tid> -A 20
# 5. 定位问题代码
# 死循环、频繁 GC、锁竞争
四、性能优化
4.1 减少对象创建
// ❌ 频繁创建对象
for (int i = 0; i < 10000; i++) {
StringBuilder sb = new StringBuilder();
sb.append(i);
}
// ✅ 复用对象
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.setLength(0);
sb.append(i);
}
4.2 使用基本类型
// ❌ 包装类型
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
list.add(i); // 自动装箱
}
// ✅ 基本类型
int[] arr = new int[10000];
for (int i = 0; i < 10000; i++) {
arr[i] = i;
}
4.3 字符串优化
// ❌ 字符串拼接
String s = "";
for (int i = 0; i < 10000; i++) {
s += i;
}
// ✅ StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
String s = sb.toString();
4.4 集合优化
// ❌ 默认容量,频繁扩容
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
list.add(String.valueOf(i));
}
// ✅ 预估容量
List<String> list = new ArrayList<>(10000);
for (int i = 0; i < 10000; i++) {
list.add(String.valueOf(i));
}
五、监控工具
5.1 命令行工具
# 进程信息
jps -lvm
# 堆信息
jstat -gc <pid> 1000 10
# 线程栈
jstack <pid>
# 堆 dump
jmap -dump:format=b,file=heap.hprof <pid>
# 类信息
jmap -histo <pid> | head -20
5.2 可视化工具
# JDK 自带
jconsole # 监控
jvisualvm # 综合分析
# 第三方
MAT # 内存分析
GCViewer # GC 日志
Arthas # 诊断工具
5.3 Arthas 使用
# 启动
java -jar arthas-boot.jar
# 查看线程
thread
# 查看堆
dashboard
# 方法耗时
trace com.example.Service method
# 参数查看
watch com.example.Service method '{params, returnObj}'
六、G1 调优
6.1 G1 参数
# 启用 G1
-XX:+UseG1GC
# 最大停顿时间
-XX:MaxGCPauseMillis=200
# 堆大小(建议固定)
-Xms4g -Xmx4g
# Region 大小(自动计算)
# -XX:G1HeapRegionSize=4m
# 晋升阈值
-XX:MaxTenuringThreshold=10
# 并发线程
-XX:ParallelGCThreads=8
-XX:ConcGCThreads=2
6.2 G1 日志分析
# G1 GC 日志
[GC pause (G1 Evacuation Pause) (young)
, 0.0123456 secs]
# 字段说明
// GC 类型:young / mixed / full
// 耗时:0.0123456 secs
6.3 G1 调优步骤
1. 设置 MaxGCPauseMillis 目标
2. 固定堆大小(-Xms = -Xmx)
3. 监控 GC 日志
4. 调整 Region 大小
5. 调整并发线程数
6. 优化代码减少对象创建
七、最佳实践
7.1 参数配置
# Web 应用(低延迟)
-Xms4g -Xmx4g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:+ParallelRefProcEnabled
# 批处理(高吞吐)
-Xms8g -Xmx8g
-XX:+UseParallelGC
-XX:MaxGCPauseMillis=0
-XX:GCTimeRatio=99
# 高并发
-Xms4g -Xmx4g
-Xss256k
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
7.2 监控告警
# 监控指标
- 堆使用率 > 80% 告警
- Full GC 间隔 < 5 分钟告警
- GC 耗时 > 1 秒告警
- 线程数 > 阈值告警
# 监控工具
Prometheus + Grafana
ELK(日志分析)
APM(应用性能监控)
八、总结
JVM 调优核心要点:
| 主题 | 关键参数 | 工具 |
|---|---|---|
| 堆内存 | -Xms, -Xmx, -Xmn | jmap, MAT |
| GC | -XX:+UseG1GC, MaxGCPauseMillis | GC 日志,GCViewer |
| 线程 | -Xss | jstack |
| 元空间 | MetaspaceSize | jstat |
| 监控 | -Xloggc | jconsole, Arthas |
JVM 调优是系统工程,需要监控→分析→调整→验证的持续迭代。