JVM 线上调优与排查指南
目录
- 第一章:JVM 调优概述
- 第二章:常用 JVM 参数
- 第三章:内存调优
- 第四章:GC 调优
- 第五章:性能监控命令
- 第六章:问题排查流程
- 第七章:实战案例
- 第八章:命令速查表
- 第九章:监控脚本
- 第十章:生产环境配置
第一章:JVM 调优概述
1.1 JVM 调优的目标
- 提高吞吐量:减少 GC 时间,提高应用处理能力
- 降低延迟:减少 GC 停顿时间,提高响应速度
- 减少内存占用:优化内存使用,避免内存泄漏
- 提高稳定性:避免 OOM,提高系统稳定性
1.2 JVM 调优的原则
- 先监控后调优:基于监控数据进行调优
- 逐步调优:一次只调整一个参数
- 测试验证:调优后要进行充分测试
- 记录对比:记录调优前后的性能数据
1.3 JVM 调优的步骤
- 性能监控:使用工具监控 JVM 性能
- 问题识别:识别性能瓶颈和问题
- 参数调优:调整 JVM 参数
- 效果验证:验证调优效果
- 持续监控:持续监控性能变化
第二章:常用 JVM 参数
2.1 内存相关参数
堆内存参数
# 设置堆内存初始大小
-Xms2g# 设置堆内存最大大小
-Xmx4g# 设置新生代大小
-Xmn1g# 设置新生代中 Eden 和 Survivor 的比例
-XX:SurvivorRatio=8# 设置老年代和新生代的比例
-XX:NewRatio=2
非堆内存参数
# 设置元空间初始大小
-XX:MetaspaceSize=256m# 设置元空间最大大小
-XX:MaxMetaspaceSize=512m# 设置直接内存大小
-XX:MaxDirectMemorySize=1g
2.2 GC 相关参数
GC 选择参数
# 使用 G1 垃圾回收器
-XX:+UseG1GC# 使用 Parallel GC
-XX:+UseParallelGC# 使用 CMS GC
-XX:+UseConcMarkSweepGC# 使用 ZGC
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
GC 调优参数
# 设置 GC 线程数
-XX:ParallelGCThreads=8# 设置 G1 最大停顿时间
-XX:MaxGCPauseMillis=200# 设置 G1 区域大小
-XX:G1HeapRegionSize=16m# 设置 CMS 并发线程数
-XX:ConcGCThreads=4
2.3 监控和日志参数
GC 日志参数
# 启用 GC 日志
-XX:+PrintGC# 启用详细 GC 日志
-XX:+PrintGCDetails# 启用 GC 时间戳
-XX:+PrintGCTimeStamps# 启用 GC 日期戳
-XX:+PrintGCDateStamps# 设置 GC 日志文件
-Xloggc:/path/to/gc.log# 启用 GC 日志轮转
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=100M
内存转储参数
# 发生 OOM 时自动生成堆转储
-XX:+HeapDumpOnOutOfMemoryError# 设置堆转储文件路径
-XX:HeapDumpPath=/path/to/heapdump.hprof# 发生 OOM 时自动生成堆转储
-XX:+HeapDumpBeforeFullGC
2.4 性能调优参数
JIT 编译参数
# 启用分层编译
-XX:+TieredCompilation# 设置编译阈值
-XX:CompileThreshold=10000# 启用方法内联
-XX:+AggressiveOpts
其他性能参数
# 启用压缩指针
-XX:+UseCompressedOops# 启用压缩类指针
-XX:+UseCompressedClassPointers# 设置大对象阈值
-XX:PretenureSizeThreshold=1m
第三章:内存调优
3.1 堆内存调优
堆内存大小设置
# 生产环境推荐配置
-Xms4g -Xmx4g -Xmn2g# 高并发应用配置
-Xms8g -Xmx8g -Xmn4g# 内存受限环境配置
-Xms1g -Xmx2g -Xmn512m
新生代调优
# Eden 和 Survivor 比例调优
-XX:SurvivorRatio=8 # Eden:Survivor = 8:1:1# 新生代和老年代比例调优
-XX:NewRatio=2 # 老年代:新生代 = 2:1
3.2 元空间调优
元空间大小设置
# 设置元空间初始大小
-XX:MetaspaceSize=256m# 设置元空间最大大小
-XX:MaxMetaspaceSize=512m# 设置元空间增长阈值
-XX:MetaspaceSizeThreshold=128m
3.3 直接内存调优
直接内存设置
# 设置直接内存大小
-XX:MaxDirectMemorySize=1g# 启用直接内存监控
-XX:+PrintNMTStatistics
第四章:GC 调优
4.1 G1 GC 调优
G1 GC 基本参数
# 启用 G1 GC
-XX:+UseG1GC# 设置最大停顿时间
-XX:MaxGCPauseMillis=200# 设置 G1 区域大小
-XX:G1HeapRegionSize=16m# 设置并发标记线程数
-XX:ConcGCThreads=4
G1 GC 高级参数
# 设置 G1 混合 GC 阈值
-XX:G1MixedGCCountTarget=8# 设置 G1 混合 GC 停顿时间
-XX:G1MixedGCLiveThresholdPercent=85# 启用 G1 字符串去重
-XX:+UseStringDeduplication
4.2 Parallel GC 调优
Parallel GC 参数
# 启用 Parallel GC
-XX:+UseParallelGC# 设置并行 GC 线程数
-XX:ParallelGCThreads=8# 设置并行 GC 停顿时间
-XX:MaxGCPauseMillis=200
4.3 CMS GC 调优
CMS GC 参数
# 启用 CMS GC
-XX:+UseConcMarkSweepGC# 设置 CMS 并发线程数
-XX:ConcGCThreads=4# 设置 CMS 触发阈值
-XX:CMSInitiatingOccupancyFraction=70# 启用 CMS 并发预清理
-XX:+CMSPrecleaningEnabled
第五章:性能监控命令
5.1 jps 命令
基本用法
# 列出所有 Java 进程
jps# 列出所有 Java 进程的详细信息
jps -l# 列出所有 Java 进程的完整信息
jps -v# 列出所有 Java 进程的完整信息
jps -m
输出示例
$ jps -l
12345 com.example.Application
67890 org.springframework.boot.loader.JarLauncher
5.2 jstat 命令
基本用法
# 监控 GC 情况
jstat -gc <pid> 1s# 监控 GC 详细情况
jstat -gcutil <pid> 1s# 监控类加载情况
jstat -class <pid> 1s# 监控编译情况
jstat -compiler <pid> 1s
输出示例
$ jstat -gc 12345 1sS0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
1024.0 1024.0 0.0 0.0 8192.0 1024.0 20480.0 1024.0 256.0 128.0 32.0 16.0 5 0.123 1 0.456 0.579
5.3 jmap 命令
基本用法
# 生成堆转储文件
jmap -dump:format=b,file=heapdump.hprof <pid># 查看堆内存使用情况
jmap -histo <pid># 查看堆内存使用情况(详细)
jmap -histo:live <pid># 查看类加载器信息
jmap -clstats <pid>
输出示例
$ jmap -histo 12345num #instances #bytes class name
----------------------------------------------1: 12345 1234567 java.lang.String2: 6789 890123 java.util.HashMap$Node3: 4567 567890 java.lang.Object[]
5.4 jstack 命令
基本用法
# 生成线程转储文件
jstack <pid> > threaddump.txt# 生成线程转储文件(包含锁信息)
jstack -l <pid> > threaddump.txt# 生成线程转储文件(包含本地变量)
jstack -F <pid> > threaddump.txt
输出示例
$ jstack 12345
"main" #1 prio=5 os_prio=0 tid=0x00007f8b8c001000 nid=0x1234 waiting on condition [0x00007f8b8d000000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for <0x00000000c0000000> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
5.5 jinfo 命令
基本用法
# 查看 JVM 参数
jinfo <pid># 查看特定 JVM 参数
jinfo -flag MaxHeapSize <pid># 动态修改 JVM 参数
jinfo -flag +PrintGC <pid>
输出示例
$ jinfo 12345
Attaching to process ID 12345, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 11.0.1+13-LTS
Java System Properties:
java.vm.name = OpenJDK 64-Bit Server VM
java.vm.version = 11.0.1+13-LTS
java.vm.vendor = Oracle Corporation
5.6 jcmd 命令
基本用法
# 列出所有可用的命令
jcmd <pid> help# 生成堆转储文件
jcmd <pid> GC.run_finalization# 生成线程转储文件
jcmd <pid> Thread.print# 生成类直方图
jcmd <pid> GC.class_histogram# 查看 JVM 参数
jcmd <pid> VM.flags
第六章:问题排查流程
6.1 内存问题排查
内存泄漏排查步骤
-
监控内存使用情况
# 使用 jstat 监控内存使用 jstat -gc <pid> 1s
-
生成堆转储文件
# 使用 jmap 生成堆转储 jmap -dump:format=b,file=heapdump.hprof <pid>
-
分析堆转储文件
- 使用 Eclipse MAT 分析堆转储文件
- 查找内存泄漏的根源
- 分析对象引用关系
OOM 问题排查步骤
-
查看 OOM 错误信息
# 查看应用日志 tail -f application.log
-
分析堆转储文件
# 如果配置了自动生成堆转储 ls -la /path/to/heapdump.hprof
-
调整内存参数
# 增加堆内存大小 -Xms4g -Xmx4g
6.2 GC 问题排查
GC 频繁问题排查
-
监控 GC 情况
# 使用 jstat 监控 GC jstat -gc <pid> 1s
-
分析 GC 日志
# 启用 GC 日志 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
-
调整 GC 参数
# 调整新生代大小 -Xmn2g# 调整 GC 策略 -XX:+UseG1GC -XX:MaxGCPauseMillis=200
GC 停顿时间过长问题排查
-
监控 GC 停顿时间
# 使用 jstat 监控 GC 时间 jstat -gc <pid> 1s
-
分析 GC 日志
# 查看 GC 日志中的停顿时间 grep "Total time" gc.log
-
调整 GC 参数
# 使用 G1 GC 减少停顿时间 -XX:+UseG1GC -XX:MaxGCPauseMillis=100
6.3 线程问题排查
线程死锁排查
-
生成线程转储文件
# 使用 jstack 生成线程转储 jstack <pid> > threaddump.txt
-
分析线程转储文件
- 查找死锁信息
- 分析线程状态
- 查看锁等待情况
线程阻塞排查
-
监控线程状态
# 使用 jstack 查看线程状态 jstack <pid> | grep "BLOCKED"
-
分析阻塞原因
- 查看线程堆栈信息
- 分析锁竞争情况
- 查找阻塞的根源
6.4 性能问题排查
CPU 使用率过高排查
-
监控 CPU 使用情况
# 使用 top 命令监控 top -p <pid>
-
生成线程转储文件
# 使用 jstack 生成线程转储 jstack <pid> > threaddump.txt
-
分析热点方法
# 使用 jcmd 生成类直方图 jcmd <pid> GC.class_histogram
响应时间过长排查
-
监控应用性能
# 使用 APM 工具监控 # 或者使用 jstat 监控 GC 情况 jstat -gc <pid> 1s
-
分析性能瓶颈
- 分析 GC 情况
- 查看线程状态
- 分析内存使用情况
第七章:实战案例
7.1 内存泄漏排查案例
问题描述
应用运行一段时间后出现 OOM 错误,需要排查内存泄漏问题。
排查步骤
-
监控内存使用情况
# 使用 jstat 监控内存使用 jstat -gc 12345 1s
-
生成堆转储文件
# 使用 jmap 生成堆转储 jmap -dump:format=b,file=heapdump.hprof 12345
-
分析堆转储文件
- 使用 Eclipse MAT 分析
- 发现大量 String 对象
- 分析对象引用关系
-
定位问题代码
- 查找创建大量 String 的代码
- 分析字符串拼接逻辑
- 优化字符串处理
解决方案
// 问题代码
String result = "";
for (int i = 0; i < 10000; i++) {result += "data" + i;
}// 优化后代码
StringBuilder result = new StringBuilder();
for (int i = 0; i < 10000; i++) {result.append("data").append(i);
}
7.2 GC 频繁问题排查案例
问题描述
应用 GC 频繁,影响性能,需要优化 GC 策略。
排查步骤
-
监控 GC 情况
# 使用 jstat 监控 GC jstat -gc 12345 1s
-
分析 GC 日志
# 查看 GC 日志 tail -f gc.log
-
调整 GC 参数
# 调整新生代大小 -Xmn2g# 使用 G1 GC -XX:+UseG1GC -XX:MaxGCPauseMillis=200
解决方案
# 优化后的 JVM 参数
-Xms4g -Xmx4g -Xmn2g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
7.3 线程死锁排查案例
问题描述
应用出现线程死锁,需要排查死锁问题。
排查步骤
-
生成线程转储文件
# 使用 jstack 生成线程转储 jstack 12345 > threaddump.txt
-
分析线程转储文件
# 查看死锁信息 grep -A 20 "Found Java-level deadlock" threaddump.txt
-
定位死锁代码
- 分析死锁的线程
- 查看锁的获取顺序
- 修改代码避免死锁
解决方案
// 问题代码 - 可能导致死锁
public void method1() {synchronized (lock1) {synchronized (lock2) {// 业务逻辑}}
}public void method2() {synchronized (lock2) {synchronized (lock1) {// 业务逻辑}}
}// 优化后代码 - 避免死锁
public void method1() {synchronized (lock1) {synchronized (lock2) {// 业务逻辑}}
}public void method2() {synchronized (lock1) { // 保持相同的锁顺序synchronized (lock2) {// 业务逻辑}}
}
总结
JVM 线上调优关键要点
- 监控先行:先监控再调优,基于数据做决策
- 参数调优:合理设置内存、GC 等参数
- 问题排查:掌握各种排查工具和方法
- 持续优化:持续监控和优化性能
常用调优参数总结
# 生产环境推荐配置
-Xms4g -Xmx4g -Xmn2g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/heapdump.hprof
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/path/to/gc.log
排查工具总结
- jps:查看 Java 进程
- jstat:监控 JVM 统计信息
- jmap:生成堆转储文件
- jstack:生成线程转储文件
- jinfo:查看和修改 JVM 参数
- jcmd:综合诊断命令
通过掌握这些调优命令和排查方法,可以有效地解决 JVM 线上问题,提高应用性能和稳定性。
第八章:命令速查表
8.1 进程管理命令
jps - Java 进程查看
# 列出所有 Java 进程
jps# 列出所有 Java 进程的详细信息
jps -l# 列出所有 Java 进程的完整信息
jps -v# 列出所有 Java 进程的完整信息
jps -m
jinfo - JVM 参数查看和修改
# 查看 JVM 参数
jinfo <pid># 查看特定 JVM 参数
jinfo -flag MaxHeapSize <pid># 动态修改 JVM 参数
jinfo -flag +PrintGC <pid>
8.2 内存监控命令
jstat - JVM 统计信息监控
# 监控 GC 情况
jstat -gc <pid> 1s# 监控 GC 详细情况
jstat -gcutil <pid> 1s# 监控类加载情况
jstat -class <pid> 1s# 监控编译情况
jstat -compiler <pid> 1s# 监控内存使用情况
jstat -gccapacity <pid> 1s
jmap - 内存映射和堆转储
# 生成堆转储文件
jmap -dump:format=b,file=heapdump.hprof <pid># 查看堆内存使用情况
jmap -histo <pid># 查看堆内存使用情况(详细)
jmap -histo:live <pid># 查看类加载器信息
jmap -clstats <pid># 查看堆内存使用情况
jmap -heap <pid>
8.3 线程监控命令
jstack - 线程转储
# 生成线程转储文件
jstack <pid> > threaddump.txt# 生成线程转储文件(包含锁信息)
jstack -l <pid> > threaddump.txt# 生成线程转储文件(包含本地变量)
jstack -F <pid> > threaddump.txt
8.4 综合诊断命令
jcmd - 综合诊断工具
# 列出所有可用的命令
jcmd <pid> help# 生成堆转储文件
jcmd <pid> GC.run_finalization# 生成线程转储文件
jcmd <pid> Thread.print# 生成类直方图
jcmd <pid> GC.class_histogram# 查看 JVM 参数
jcmd <pid> VM.flags# 查看系统属性
jcmd <pid> VM.system_properties# 查看命令行参数
jcmd <pid> VM.command_line# 查看 JVM 版本信息
jcmd <pid> VM.version# 查看 JVM 启动时间
jcmd <pid> VM.uptime
8.5 常用排查命令组合
快速诊断
# 获取进程信息
jps -l# 查看 JVM 参数
jinfo <pid># 监控内存使用
jstat -gc <pid> 1s 5# 查看线程状态
jstack <pid> | grep "java.lang.Thread.State" | sort | uniq -c
深度诊断
# 生成堆转储
jmap -dump:format=b,file=heapdump.hprof <pid># 生成线程转储
jstack <pid> > threaddump.txt# 生成类直方图
jmap -histo:live <pid> > class_histogram.txt# 查看 GC 信息
jstat -gc <pid> 1s 10 > gc_info.txt
第九章:监控脚本
9.1 内存监控脚本
#!/bin/bash
PID=$1
while true; doecho "=== $(date) ==="jstat -gc $PID 1s 1echo ""sleep 5
done
9.2 GC 监控脚本
#!/bin/bash
PID=$1
while true; doecho "=== $(date) ==="jstat -gcutil $PID 1s 1echo ""sleep 10
done
9.3 线程监控脚本
#!/bin/bash
PID=$1
while true; doecho "=== $(date) ==="jstack $PID | grep "java.lang.Thread.State" | sort | uniq -cecho ""sleep 30
done
9.4 综合监控脚本
#!/bin/bash
PID=$1
LOG_DIR="/var/log/jvm-monitor"
mkdir -p $LOG_DIRwhile true; doTIMESTAMP=$(date +"%Y%m%d_%H%M%S")echo "=== JVM 监控报告 - $TIMESTAMP ===" >> $LOG_DIR/jvm_monitor.log# 内存使用情况echo "--- 内存使用情况 ---" >> $LOG_DIR/jvm_monitor.logjstat -gc $PID 1s 1 >> $LOG_DIR/jvm_monitor.log# 线程状态echo "--- 线程状态 ---" >> $LOG_DIR/jvm_monitor.logjstack $PID | grep "java.lang.Thread.State" | sort | uniq -c >> $LOG_DIR/jvm_monitor.log# 类加载情况echo "--- 类加载情况 ---" >> $LOG_DIR/jvm_monitor.logjstat -class $PID 1s 1 >> $LOG_DIR/jvm_monitor.logecho "" >> $LOG_DIR/jvm_monitor.logsleep 60
done
第十章:生产环境配置
10.1 基础配置
# 内存配置
-Xms4g -Xmx4g -Xmn2g
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m# GC 配置
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m# 监控配置
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/heapdump.hprof
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/path/to/gc.log
10.2 高并发配置
# 内存配置
-Xms8g -Xmx8g -Xmn4g
-XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1g# GC 配置
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:G1HeapRegionSize=32m
-XX:ConcGCThreads=8# 性能配置
-XX:+UseCompressedOops
-XX:+UseCompressedClassPointers
-XX:+TieredCompilation
10.3 内存受限配置
# 内存配置
-Xms1g -Xmx2g -Xmn512m
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m# GC 配置
-XX:+UseG1GC
-XX:MaxGCPauseMillis=300
-XX:G1HeapRegionSize=8m# 优化配置
-XX:+UseCompressedOops
-XX:+AggressiveOpts
10.4 微服务配置
# 内存配置
-Xms512m -Xmx1g -Xmn256m
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m# GC 配置
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=8m# 监控配置
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heapdump.hprof
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/tmp/gc.log
10.5 大数据处理配置
# 内存配置
-Xms16g -Xmx32g -Xmn8g
-XX:MetaspaceSize=1g -XX:MaxMetaspaceSize=2g# GC 配置
-XX:+UseG1GC
-XX:MaxGCPauseMillis=500
-XX:G1HeapRegionSize=64m
-XX:ConcGCThreads=16# 性能配置
-XX:+UseCompressedOops
-XX:+UseCompressedClassPointers
-XX:+TieredCompilation
-XX:+AggressiveOpts
10.6 配置选择指南
根据应用类型选择配置
- Web 应用:使用基础配置或高并发配置
- 微服务:使用微服务配置
- 大数据处理:使用大数据处理配置
- 内存受限环境:使用内存受限配置
根据硬件资源选择配置
- 8GB 内存:使用基础配置
- 16GB 内存:使用高并发配置
- 32GB+ 内存:使用大数据处理配置
- 4GB 以下内存:使用内存受限配置
根据性能要求选择配置
- 低延迟要求:使用 G1 GC,设置较小的停顿时间
- 高吞吐量要求:使用 Parallel GC
- 平衡性能:使用 G1 GC,设置适中的停顿时间
注意事项
- 权限要求:某些命令需要与目标进程相同的用户权限
- 性能影响:频繁使用监控命令可能影响应用性能
- 文件大小:堆转储文件可能很大,注意磁盘空间
- 版本兼容:不同 JDK 版本的命令参数可能不同
- 生产环境:在生产环境使用时要谨慎,避免影响业务
- 监控频率:监控频率不宜过高,避免影响应用性能
- 日志管理:定期清理监控日志,避免磁盘空间不足
- 参数调优:调优参数时要逐步调整,避免一次性大幅修改
记住:JVM 调优是一个持续的过程,需要根据实际应用情况进行调整和优化。在生产环境中,建议先在测试环境验证调优效果,然后再应用到生产环境。