JVM性能调优
一、调优核心目标
- 减少GC停顿时间(降低延迟)
- 提高吞吐量(单位时间处理更多请求)
- 合理控制内存占用(避免频繁的Full GC或内存溢出OOM)
- 优化资源利用率(合理分配内存和CPU资源)
二、基础内存参数设置
- 初始堆内存(
-Xms
):设置JVM启动时分配的堆内存大小。 - 最大堆内存(
-Xmx
):设置JVM可用的最大堆内存大小。 - 新生代比例(
-XX:NewRatio
):设置老年代与新生代的比例。默认值为2,表示老年代是新生代的两倍。 - Survivor区比例(
-XX:SurvivorRatio
):设置Eden区与Survivor区的比例。默认值为8,表示每个Survivor区是Eden区的1/8。 - 元空间初始大小(
-XX:MetaspaceSize
):避免频繁扩展元空间。 - 元空间最大大小(
-XX:MaxMetaspaceSize
):防止元空间占用过多内存。 建议:将-Xms和-Xmx设置为相同的值,避免动态扩展堆内存导致的性能波动。
1. 堆内存配置
-Xms4g -Xmx4g # 初始堆=最大堆(避免动态扩容导致的性能波动)
-XX:NewRatio=2 # 新生代:老年代=1:2(默认值,适合通用场景)
-XX:SurvivorRatio=8 # Eden:Survivor=8:1:1(默认值)
2. 非堆内存配置
-XX:MetaspaceSize=256m # 元空间初始大小(替代PermGen)
-XX:MaxMetaspaceSize=512m # 元空间上限
-XX:ReservedCodeCacheSize=256m # JIT编译代码缓存
三、垃圾回收器选择与参数
常见的垃圾回收器包括:
- 串行收集器(Serial GC):适合单核CPU和小内存应用。
- 并行收集器(Parallel GC):适合多核CPU,目标是高吞吐量。
- 并发收集器(Concurrent Mark Sweep, CMS):适合低延迟场景,但可能会出现“并发模式失败”。
- G1收集器(Garbage-First GC):适合大堆内存和低延迟需求。
- ZGC和Shenandoah GC:适合超低延迟和超大堆内存场景。
注意:除了通用参数,每种垃圾回收器的参数基本都不一样。
1. G1垃圾回收器(JDK9+默认)
-XX:+UseG1GC # 启用G1
-XX:MaxGCPauseMillis=200 # 目标最大停顿时间(毫秒)
-XX:G1NewSizePercent=5 # 新生代最小占比
-XX:G1MaxNewSizePercent=60 # 新生代最大占比
-XX:InitiatingHeapOccupancyPercent=45 # 触发并发标记的老年代占比阈值
2. CMS垃圾回收器(JDK8推荐)
-XX:+UseConcMarkSweepGC # 启用CMS
-XX:CMSInitiatingOccupancyFraction=75 # 老年代使用率触发CMS收集
-XX:+UseCMSInitiatingOccupancyOnly # 固定触发阈值
-XX:+ExplicitGCInvokesConcurrent # System.gc()触发CMS而非Full GC
3. 通用参数
-XX:+DisableExplicitGC # 禁止显式调用System.gc()(谨慎使用)
-XX:+PrintGCDetails -XX:+PrintGCDateStamps # 打印详细GC日志
-Xloggc:/path/to/gc.log # 输出GC日志文件
- 设置GC日志:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log
。JDK 9有新推荐使用的配置参数-Xlog:gc*,更加灵活且性能更好。 - 开启GC日志可以帮助分析垃圾回收的性能问题,但也会对系统性能产生一定的影响,在频繁出发GC的应用(如高并发场景)中使用时要注意观察性能影响。
四、诊断与监控参数
1. 内存溢出分析
-XX:+HeapDumpOnOutOfMemoryError # OOM时生成堆转储文件(dump文件)
-XX:HeapDumpPath=/path/to/dump.hprof # 堆转储路径
2. JIT编译监控
-XX:+PrintCompilation # 打印JIT编译日志
-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation # 详细编译日志
3. Native内存跟踪
-XX:NativeMemoryTracking=detail # 启用Native内存跟踪
jcmd <pid> VM.native_memory detail # 查看统计(需NMT启用)
五、调优步骤示例
场景:Web服务频繁Full GC
-
收集数据
jstat -gcutil <pid> 1000 10 # 每1秒采样GC状态,共10次 jmap -histo:live <pid> > heap_histo.txt # 对象分布统计
-
分析问题
- 发现老年代占用率快速上升
- 存在大对象直接进入老年代(如缓存未分页)
-
参数调整
# 增大堆大小并优化对象分配 -Xms8g -Xmx8g -XX:PretenureSizeThreshold=4m # >4M对象直接进老年代(避免Eden拷贝) -XX:+UseG1GC -XX:MaxGCPauseMillis=150 # 切换为G1优化延迟
-
验证效果
- Full GC频率从每小时10次降至1次
- 平均响应时间下降30%
六、调优注意事项
-
避免过度调优
- 优先通过代码优化(如减少内存泄漏、合理使用缓存)解决问题
- JVM参数调优是最后手段
-
分阶段验证
- 每次只修改1-2个参数,观察效果
- 使用压测工具(JMeter/Gatling)验证性能变化
-
关注版本差异
- JDK8/11/17的默认GC和内存策略不同
- 新版本优先使用G1/ZGC/Shenandoah
七、常用的调优工具
总结
JVM调优没有“万能配置”,核心原则是:
- 基于监控数据驱动调优
- 优先解决代码问题,再调整参数
- 平衡吞吐量、延迟和内存占用
- 在开发/测试环境充分验证后再上线