性能调优
JVM参数
常用JVM参数?
答案:
堆内存设置:
bash
-Xms2g # 初始堆大小
-Xmx2g # 最大堆大小(建议与Xms相同)
-Xmn1g # 新生代大小
-Xss256k # 栈大小
-XX:MetaspaceSize=128m # 元空间初始大小
-XX:MaxMetaspaceSize=256m # 元空间最大大小垃圾收集器:
bash
-XX:+UseG1GC # 使用G1收集器
-XX:MaxGCPauseMillis=200 # 最大停顿时间
-XX:ParallelGCThreads=8 # 并行GC线程数
-XX:ConcGCThreads=2 # 并发GC线程数GC日志:
bash
-XX:+PrintGCDetails # 打印GC详情
-XX:+PrintGCDateStamps # 打印GC时间戳
-Xloggc:gc.log # GC日志文件OOM处理:
bash
-XX:+HeapDumpOnOutOfMemoryError # OOM时生成堆转储
-XX:HeapDumpPath=/tmp/heapdump.hprof
-XX:OnOutOfMemoryError="sh ~/restart.sh" # OOM时执行脚本性能监控:
bash
-XX:+PrintFlagsFinal # 打印所有JVM参数
-XX:+UnlockDiagnosticVMOptions # 解锁诊断参数性能分析工具
常用的性能分析工具?
答案:
1. jps - 查看Java进程
bash
jps -l # 显示完整类名
jps -v # 显示JVM参数2. jstat - 查看JVM统计信息
bash
jstat -gc 12345 1000 10 # 每秒输出GC信息,共10次
jstat -gcutil 12345 # GC统计百分比
jstat -gccause 12345 # GC原因3. jmap - 生成堆转储
bash
jmap -heap 12345 # 查看堆信息
jmap -histo 12345 # 查看对象统计
jmap -dump:format=b,file=heap.hprof 12345 # 生成堆转储4. jstack - 查看线程堆栈
bash
jstack 12345 # 查看线程堆栈
jstack -l 12345 # 查看锁信息5. jinfo - 查看和修改JVM参数
bash
jinfo -flags 12345 # 查看JVM参数
jinfo -flag MaxHeapSize 12345 # 查看具体参数6. VisualVM
- 图形化监控工具
- 监控CPU、内存、线程
- 生成和分析堆转储
7. JProfiler
- 商业性能分析工具
- CPU分析、内存分析、线程分析
8. Arthas
- 阿里开源的Java诊断工具
- 在线诊断,无需重启
bash
# 启动Arthas
java -jar arthas-boot.jar
# 常用命令
dashboard # 仪表盘
thread # 线程信息
jvm # JVM信息
memory # 内存信息
gc # GC信息性能调优
如何进行JVM调优?
答案:
1. 确定目标
- 降低延迟
- 提高吞吐量
- 减少Full GC
2. 收集数据
- GC日志
- 堆转储
- 线程dump
3. 分析问题
- GC频繁
- 内存泄漏
- 线程死锁
- CPU飙高
4. 调整参数
- 堆大小
- 垃圾收集器
- GC参数
5. 验证效果
- 压力测试
- 监控指标
常见性能问题及解决?
答案:
1. 内存溢出(OOM)
原因:
- 堆内存不足
- 内存泄漏
- 创建大对象
解决:
bash
# 增加堆内存
-Xms4g -Xmx4g
# 分析堆转储
jmap -dump:format=b,file=heap.hprof 12345
# 使用MAT分析heap.hprof2. GC频繁
原因:
- 堆内存过小
- 对象创建过多
- 内存泄漏
解决:
bash
# 增加堆内存
-Xms2g -Xmx2g
# 调整新生代大小
-Xmn1g
# 调整Survivor比例
-XX:SurvivorRatio=83. Full GC频繁
原因:
- 老年代空间不足
- 元空间不足
- System.gc()调用
解决:
bash
# 增加老年代
-Xms4g -Xmx4g -Xmn1g # 老年代3g
# 增加元空间
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
# 禁用System.gc()
-XX:+DisableExplicitGC4. CPU飙高
排查步骤:
bash
# 1. 找到Java进程
jps
# 2. 找到占用CPU高的线程
top -Hp 12345
# 3. 转换线程ID为16进制
printf "%x\n" 1234
# 4. 查看线程堆栈
jstack 12345 | grep -A 20 4d25. 内存泄漏
排查步骤:
bash
# 1. 生成堆转储
jmap -dump:format=b,file=heap1.hprof 12345
# 等待一段时间
jmap -dump:format=b,file=heap2.hprof 12345
# 2. 使用MAT对比分析
# 查找持续增长的对象常见原因:
- 集合类未清理
- 静态集合持有对象
- 监听器未移除
- 数据库连接未关闭
调优案例
案例1:接口响应慢
问题:接口响应时间从100ms增加到5s
排查:
bash
# 1. 查看GC日志
jstat -gcutil 12345 1000
# 发现Full GC频繁,每次耗时3s原因:老年代空间不足,频繁Full GC
解决:
bash
# 增加堆内存
-Xms4g -Xmx4g
# 使用G1收集器
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200效果:响应时间恢复到100ms
案例2:内存持续增长
问题:应用运行一段时间后OOM
排查:
bash
# 1. 生成堆转储
jmap -dump:format=b,file=heap.hprof 12345
# 2. 使用MAT分析
# 发现ArrayList持有大量对象原因:静态List未清理,导致内存泄漏
解决:
java
// 定期清理
public class Cache {
private static List<Data> cache = new ArrayList<>();
public void add(Data data) {
cache.add(data);
// 添加清理逻辑
if (cache.size() > 10000) {
cache.clear();
}
}
}练习题
- 如何排查CPU飙高问题?
- 如何排查内存泄漏?
- 如何选择合适的垃圾收集器?
- 生产环境应该设置哪些JVM参数?