JVM 性能调优实战
原创2024/1/12大约 3 分钟
JVM 性能调优实战
JVM 性能调优是 Java 开发中的重要技能,本文介绍实用的调优方法和工具。
JVM 内存结构
运行时数据区
+-------------------+
| 方法区 (Method) | <- 类信息、常量池
+-------------------+
| 堆 (Heap) | <- 对象实例
| +-------------+ |
| | 新生代 | |
| | +------+ | |
| | |Eden | | |
| | +------+ | |
| | |S0|S1| | |
| | +--+--+ | |
| +-------------+ |
| | 老年代 | |
| +-------------+ |
+-------------------+
| JVM 栈 (Stack) | <- 局部变量、方法调用
+-------------------+
| 本地方法栈 |
+-------------------+
| 程序计数器 |
+-------------------+垃圾回收器
G1 GC(推荐)
# G1 GC 参数设置
java -Xms4g -Xmx4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:G1HeapRegionSize=16m \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-Xloggc:gc.log \
-jar app.jarZGC(低延迟)
# ZGC 参数设置
java -Xms16g -Xmx16g \
-XX:+UseZGC \
-XX:+ZGenerational \
-XX:+PrintGCDetails \
-jar app.jar常用JVM参数
堆内存设置
-Xms2g # 初始堆大小
-Xmx2g # 最大堆大小
-Xmn1g # 新生代大小
-XX:MetaspaceSize=256m # 元空间初始大小
-XX:MaxMetaspaceSize=512m # 元空间最大大小GC 日志
-XX:+PrintGCDetails # 打印 GC 详情
-XX:+PrintGCDateStamps # 打印 GC 时间戳
-XX:+PrintHeapAtGC # GC 前后打印堆信息
-Xloggc:/path/to/gc.log # GC 日志文件
-XX:+UseGCLogFileRotation # 日志轮转
-XX:NumberOfGCLogFiles=10 # 日志文件数量
-XX:GCLogFileSize=100M # 单个日志文件大小性能监控
-XX:+PrintCommandLineFlags # 打印命令行参数
-XX:+HeapDumpOnOutOfMemoryError # OOM 时生成 dump
-XX:HeapDumpPath=/path/to/dump # dump 文件路径性能分析工具
1. jstat - 监控 JVM 统计信息
# 查看 GC 统计
jstat -gc <pid> 1000 10
# 查看类加载统计
jstat -class <pid>
# 查看编译统计
jstat -compiler <pid>2. jmap - 内存映射工具
# 生成堆 dump
jmap -dump:live,format=b,file=heap.bin <pid>
# 查看堆内存使用
jmap -heap <pid>
# 查看对象直方图
jmap -histo <pid> | head -203. jstack - 线程堆栈分析
# 打印线程堆栈
jstack <pid> > thread.dump
# 查找死锁
jstack -l <pid> | grep -A 10 "deadlock"4. jvisualvm - 可视化监控
启动 VisualVM 并连接到 JVM:
- 实时监控 CPU、内存使用
- 分析堆 dump
- 线程分析
- 性能剖析
常见问题排查
内存泄漏
// 问题代码示例
public class MemoryLeak {
private static List<Object> list = new ArrayList<>();
public void doSomething() {
// 对象不断添加但从不清理
list.add(new Object());
}
}
// 排查步骤
1. 使用 jmap 生成 heap dump
2. 用 MAT 分析 dump 文件
3. 查找内存占用最大的对象
4. 分析 GC Roots 路径
5. 修复代码逻辑频繁 Full GC
# 分析 GC 日志
# 查看 Full GC 频率和暂停时间
grep "Full GC" gc.log | wc -l
# 可能原因:
1. 老年代空间不足
2. 元空间不足
3. System.gc() 调用
4. 分配大对象
# 解决方案:
1. 增加堆内存
2. 调整新生代/老年代比例
3. 优化代码,减少大对象创建CPU 占用高
# 1. 找出占用 CPU 高的线程
top -H -p <pid>
# 2. 转换线程 ID 为十六进制
printf "%x\n" <thread_id>
# 3. 查看线程堆栈
jstack <pid> | grep <hex_thread_id> -A 30
# 4. 分析代码问题调优案例
案例 1: 减少 Young GC 频率
# 问题:Young GC 频率过高
# 原因:新生代空间太小
# 调整前
-Xms4g -Xmx4g -Xmn1g
# 调整后
-Xms4g -Xmx4g -Xmn2g
# 结果:Young GC 频率降低 50%案例 2: 降低 GC 停顿时间
# 问题:GC 停顿时间过长
# 原因:使用 Serial GC
# 调整前
-XX:+UseSerialGC
# 调整后
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
# 结果:GC 停顿时间从 500ms 降到 150ms最佳实践
- 设置合理的堆大小:
-Xms和-Xmx设置相同 - 选择合适的 GC: G1 GC 适合大多数场景
- 启用 GC 日志: 便于问题排查
- 定期监控: 使用 APM 工具
- 避免过早优化: 先性能测试,再针对性优化
性能调优清单
总结
JVM 性能调优需要深入理解 JVM 原理,结合实际业务场景进行调整。关键是要有监控、有数据、有分析。