一、为什么需要JVM调优
Java应用运行在JVM上,垃圾回收(GC)是影响性能的关键因素:
GC带来的问题:
- STW(Stop The World)导致应用停顿
- 频繁GC浪费CPU资源
- 内存分配不合理导致频繁GC
- OOM(内存溢出)导致应用崩溃
调优的目标:
- 降低GC停顿时间(<200ms)
- 提高吞吐量(>99%)
- 避免OOM
二、垃圾回收器详解
1. 垃圾回收器对比
| 回收器 | 线程数 | 适用场景 | 停顿时间 | |
|---|---|---|---|---|
| Serial | 单线程 | 简单高效 | 小内存(<100MB) | 100-500ms |
| Parallel | 多线程 | 高吞吐 | 后台批处理 | 100-500ms |
| CMS | 并发 | 低停顿 | Web应用 | <200ms |
| G1 | 并发 | 可预测停顿 | 大内存(>6GB) | <200ms |
| ZGC | 并发 | 亚毫秒停顿 | 超大内存(>16GB) | <10ms |
| Shenandoah | 并发 | 低停顿 | 容器环境 | <10ms |
2. 垃圾回收器选择
bash
# 选择G1回收器(推荐)
-XX:+UseG1GC
# 选择ZGC(超大内存)
-XX:+UseZGC
# 选择Parallel(批处理)
-XX:+UseParallelGC
# 选择CMS(兼容旧版本)
-XX:+UseConcMarkSweepGC
三、JVM内存配置
1. 堆内存配置
bash
# 基础配置
-Xms4g # 初始堆大小
-Xmx4g # 最大堆大小
-Xmn2g # 年轻代大小(建议占堆的1/2到1/3)
# 元空间配置
-XX:MetaspaceSize=256m # 初始元空间
-XX:MaxMetaspaceSize=512m # 最大元空间
# 线程栈配置
-Xss1m # 线程栈大小(默认1MB)
2. G1专用配置
bash
# G1配置
-XX:+UseG1GC # 使用G1回收器
-XX:MaxGCPauseMillis=200 # 最大GC停顿时间目标
-XX:G1HeapRegionSize=16m # Region大小(1MB-32MB,必须是2的幂)
-XX:InitiatingHeapOccupancyPercent=45 # 触发Mixed GC的堆占用比例
# 其他G1优化
-XX:G1ReservePercent=10 # 预留内存比例
-XX:G1MixedGCLiveThresholdPercent=85 # Old区回收阈值
3. 容器环境配置
bash
# 容器环境配置
-XX:+UseContainerSupport # 启用容器支持
-XX:InitialRAMPercentage=50 # 初始堆占比
-XX:MaxRAMPercentage=80 # 最大堆占比
-XX:MinRAMPercentage=20 # 小堆时最小占比
四、GC日志分析
1. 开启GC日志
bash
# 基础GC日志
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags
# 详细GC日志
-Xlog:gc*=debug:file=/var/log/gc-debug.log:time,uptime,level,tags
# 使用G1时打印young区详细信息
-Xlog:gc+age=debug:file=/var/log/gc-age.log:time
# 老年代详细信息
-Xlog:gc+old=debug:file=/var/log/gc-old.log:time
推荐配置:
bash
# 完整的GC日志配置
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags:filecount=10,filesize=100m \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/heapdump.hprof
2. GC日志解读
Young GC日志:
[2024-01-15T10:23:45.123+0800][info][gc] GC(12) Pause Young (Normal) 512M->128M(4G) 45.678ms
- GC(12):第12次GC
- Pause Young:Young区回收
- 512M->128M:回收前512MB,回收后128MB
- (4G):堆总大小4GB
- 45.678ms:停顿时间
Mixed GC日志:
[2024-01-15T10:23:45.123+0800][info][gc] GC(15) Pause Young (Mixed) 2G->1G(4G) 123.456ms
GC(15) Metaspace: 256M->258M(512M) 12.345ms
Full GC日志:
[2024-01-15T10:23:45.123+0800][warn][gc] GC(20) Pause Full (System.gc()) 3G->2G(4G) 456.789ms
3. GC日志分析工具
在线工具:
- GCEasy(https://gceasy.io)
- GCViewer
本地分析:
bash
# 下载GCViewer
wget https://github.com/chewiebug/GCViewer/releases/download/1.34/GCViewer-1.34.jar
# 运行
java -jar GCViewer-1.34.jar gc.log gc-report.html
关键指标:
| 指标 | 含义 | 目标值 |
|---|---|---|
| Throughput | 吞吐量 | >95% |
| GC Count | GC次数 | 越少越好 |
| Pause Time | 停顿时间 | <200ms |
| Old Gen Usage | 老年代使用率 | <80% |
五、常见GC问题
1. 频繁Young GC
原因:
- 年轻代太小
- 对象分配过快
- Survivor区太小
解决方案:
bash
# 增大年轻代
-Xmn2g -XX:SurvivorRatio=8
# 增大Eden区
-XX:SurvivorRatio=8 # Eden:Survivor=8:1
2. 频繁Full GC
原因:
- 老年代空间不足
- 内存泄漏
- 大对象直接进入老年代
诊断步骤:
bash
# 1. 查看堆使用情况
jmap -heap <pid>
# 2. 查看对象分布
jmap -histo <pid> | head -30
# 3. 生成堆转储
jmap -dump:format=b,file=heap.hprof <pid>
# 4. 分析堆转储
jhat heap.hprof
3. OOM问题
常见OOM类型:
| 类型 | 原因 | 解决方案 |
|---|---|---|
| Java heap space | 堆内存不足 | 增大-Xmx |
| Metaspace | 元空间不足 | 增大-XX:MaxMetaspaceSize |
| Unable to create new native thread | 线程太多 | 减少线程数 |
| Direct buffer memory | NIO内存不足 | 增大-XX:MaxDirectMemorySize |
六、性能调优实战
1. 典型配置
Web应用配置:
bash
# 4核8GB服务器配置
-Xms4g -Xmx4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:InitiatingHeapOccupancyPercent=45 \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=512m \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/heapdump.hprof \
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags:filecount=10,filesize=100m
大内存服务器配置(16GB以上):
bash
# 16GB服务器配置
-Xms12g -Xmx12g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=300 \
-XX:G1HeapRegionSize=32m \
-XX:InitiatingHeapOccupancyPercent=40 \
-XX:MetaspaceSize=512m \
-XX:MaxMetaspaceSize=1g \
-XX:+HeapDumpOnOutOfMemoryError \
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags:filecount=20,filesize=200m
低延迟应用配置(金融、游戏):
bash
# 低延迟配置
-Xms8g -Xmx8g \
-XX:+UseZGC \
-XX:ConcGCThreads=4 \
-XX:MaxLogFileNum=10 \
-XX:LogFileSize=100m \
-XX:+AlwaysPreTouch \
-XX:+UseLargePages \
-XX:+HeapDumpOnOutOfMemoryError
2. 容器环境配置
bash
# K8s环境配置
JAVA_OPTS="
-XX:+UseContainerSupport
-XX:InitialRAMPercentage=50
-XX:MaxRAMPercentage=80
-XX:MinRAMPercentage=20
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-Xlog:gc*:file=/var/log/gc.log
"
七、监控与诊断
1. JVisualVM
bash
# 启动JVisualVM
jvisualvm
监控内容:
- 堆内存使用
- 线程数
- CPU使用率
- GC次数和时间
2. JConsole
bash
# 启动JConsole
jconsole
3. Arthas
bash
# 安装Arthas
curl -L https://arthas.aliyun.com/install.sh | sh
# 启动
java -jar arthas-boot.jar
# 查看GC信息
dashboard -c 1
# 查看对象
sc -d ClassName
# 追踪方法执行
watch ClassName methodName '{params,returnObj,throwExp}'
4. Prometheus + Grafana
yaml
# JMX Exporter配置
---
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'jvm'
static_configs:
- targets: ['localhost:7071']
八、总结
JVM调优是Java性能优化的核心:
- 选择回收器:G1适合大多数场景
- 合理配置内存:避免频繁GC
- 分析GC日志:定位问题根源
- 监控持续:及时发现异常
最佳实践:
- 先使用默认配置,观察GC行为
- 根据GC日志调整参数
- 避免过度调优
- 做好监控和告警
个人观点,仅供参考