JVM 调优方案
一、JVM 调优核心思想
在深入具体方案之前,必须明确两个核心思想:
-
调优的目的通常是为了解决以下问题:
- GC 停顿时间过长:应用出现卡顿。
- 吞吐量下降:单位时间内处理的请求变少。
- 内存溢出:发生 OutOfMemoryError。
- CPU 负载过高:频繁的 GC 或线程竞争导致 CPU 资源紧张。
-
一切依赖于数据和场景:调优必须基于监控数据(GC 日志、堆快照、性能分析工具),并根据应用类型(Web 服务、大数据计算、交易系统)来制定策略。
二、常用 JVM 调优参数
通常将参数分为堆内存、垃圾收集器、GC 日志、其他性能相关几大类。
2.1 基础堆内存参数
# 设置初始堆大小
-Xms512m# 设置最大堆大小
-Xmx2048m# 设置年轻代大小
-Xmn512m# 设置元空间大小(Java 8+)
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m# 设置永久代大小(Java 7及之前)
-XX:PermSize=128m
-XX:MaxPermSize=256m
-
-Xms 和 -Xmx
- 说明:设置堆的初始大小和最大大小。通常将它们设为相同的值,以避免在运行时动态调整堆大小带来的性能波动。
- 示例:-Xms4g -Xmx4g (设置堆大小为 4GB)
-
-Xmn
- 说明:设置年轻代的大小。整个堆 = 年轻代 + 老年代。增大年轻代会减小老年代。这个参数对 GC 性能影响很大。
- 示例:-Xmn2g (在 4G 堆中,年轻代占 2G,老年代占 2G)。通常设置为整个堆大小的 1/3 到 1/2。
-
-XX:MetaspaceSize 和 -XX:MaxMetaspaceSize
- 说明:设置元空间的初始大小和最大大小。在 Java 8 中,永久代被移除,由元空间取代。如果动态生成类较多(如 Spring 的 CGLib),需要限制其大小以防吃光所有内存。
- 示例:-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m
-
-Xss
- 说明:设置每个线程栈的大小。如果系统有大量线程,过大的栈大小会占用过多内存。过小则可能引起 StackOverflowError。
- 示例:-Xss256k (对于大多数 Web 应用,256k 足够)
2.2 堆内存比例参数
# 新生代与老年代比例(默认约1:2)
-XX:NewRatio=2# 设置年轻代中 Eden 和 Survivor 的比例
-XX:SurvivorRatio=8 # Eden:S0:S1 = 8:1:1# 设置晋升老年代的年龄阈值
-XX:MaxTenuringThreshold=15# 设置大对象直接进入老年代的阈值
-XX:PretenureSizeThreshold=1m# 设置TLAB(线程本地分配缓冲区)大小
-XX:TLABSize=64k
2.3 垃圾收集器相关
选择并优化合适的垃圾收集器是调优的核心。
-
并行收集器(吞吐量优先)
- 参数:-XX:+UseParallelGC / -XX:+UseParallelOldGC
- 说明:JDK 8 的默认收集器,关注高吞吐量。适合后台运算、大数据处理等不需要低延迟的场景。
- 示例:
# 使用Parallel GC -XX:+UseParallelGC# 设置并行GC线程数 -XX:ParallelGCThreads=4# 设置最大GC暂停时间目标 -XX:MaxGCPauseMillis=100# 设置吞吐量目标(GC时间与总时间比例) -XX:GCTimeRatio=99
-
CMS 收集器(低延迟)
- 参数:-XX:+UseConcMarkSweepGC
- 说明:以获取最短停顿时间为目标,适用于 Web 服务、B/S 系统。在 JDK 14 中被移除。
- 示例:
# 使用CMS老年代收集器 -XX:+UseConcMarkSweepGC# 使用ParNew新生代收集器 -XX:+UseParNewGC# CMS后台线程数 -XX:ConcGCThreads=2# CMS触发百分比 -XX:CMSInitiatingOccupancyFraction=75# 开启CMS压缩 -XX:+UseCMSCompactAtFullCollection# CMS FullGC前进行压缩 -XX:CMSFullGCsBeforeCompaction=0
-
G1 收集器(推荐)
- 参数:-XX:+UseG1GC
- 说明:JDK 9 及以后的默认收集器,面向服务端、大内存、低延迟应用。它将堆划分为多个 Region,并优先回收垃圾最多的 Region。
- 示例:
# 使用G1垃圾收集器 -XX:+UseG1GC# 设置最大GC暂停时间目标 -XX:MaxGCPauseMillis=200# 设置区域大小 -XX:G1HeapRegionSize=16m# 设置并发GC线程数 -XX:ConcGCThreads=4# 设置混合GC周期收集中最大旧区域数量 -XX:G1OldCSetRegionThresholdPercent=10
-
ZGC 收集器(极致低延迟)
- 参数:-XX:+UseZGC
- 说明:新一代的低延迟垃圾收集器,目标是将停顿时间控制在 10ms 以下。适用于对延迟极其敏感的应用(如金融交易系统)。
- 示例:
# 启用 ZGC -XX:+UseZGC# 调优参数 -XX:ZAllocationSpikeTolerance=2.0 # 分配峰值容忍度 -XX:ZCollectionInterval=300 # 强制GC间隔(秒) -XX:ZFragmentationLimit=10 # 碎片化限制 -XX:ZProactive=true # 启用主动GC# 内存映射调优 -XX:ZUncommitDelay=300 # 内存未使用回收延迟(秒)
2.3 GC 日志相关
没有日志,调优就是盲人摸象。必须开启详细的 GC 日志。
-
详细 GC 日志(JDK 8 及之前)
# 开启GC日志 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps# 输出GC日志到文件 -Xloggc:/path/to/gc.log# 开启GC日志轮转 -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M -
统一日志框架(JDK 9+ 推荐)
# 详细 GC 日志 -Xlog:gc*=debug:file=/app/logs/gc-%t.log:time,uptime,level,tags:filecount=10,filesize=100M# 选择性日志(生产环境推荐) -Xlog:gc=info:file=/app/logs/gc.log:time,uptimemillis,level,tags -Xlog:gc+heap=debug -Xlog:gc+ergo*=trace -Xlog:gc+age*=debug -
高级监控参数
# 打印命令行参数 -XX:+PrintCommandLineFlags# 打印GC应用停顿时间 -XX:+PrintGCApplicationStoppedTime# 打印GC前后堆信息 -XX:+PrintHeapAtGC# 开启类加载日志 -XX:+TraceClassLoading -XX:+TraceClassUnloading# 在发生 OOM 时自动生成堆转储文件,用于事后分析 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/heapdump.hprof
2.4 JIT 编译与性能优化
# 分层编译(JDK 8 默认)
-XX:+TieredCompilation# 编译阈值调优
-XX:Tier0InvokeNotifyFreqLog=7
-XX:Tier2BackEdgeThreshold=100000
-XX:Tier3InvocationThreshold=2000
-XX:Tier3MinInvocationThreshold=100# 代码缓存大小
-XX:ReservedCodeCacheSize=512m
-XX:InitialCodeCacheSize=64m# 方法内联优化
-XX:MaxInlineSize=325
-XX:FreqInlineSize=325
-XX:InlineSmallCode=1000
三、不同场景的配置模板
3.1 开发环境配置
java -Xms512m -Xmx1g \-Xmn256m \-XX:MetaspaceSize=128m \-XX:MaxMetaspaceSize=256m \-XX:+UseG1GC \-XX:MaxGCPauseMillis=200 \-XX:+PrintGC \-XX:+PrintGCDateStamps \-Xloggc:./logs/gc.log \-jar app.jar
3.2 测试环境配置
java -Xms2g -Xmx4g \-Xmn1g \-XX:MetaspaceSize=256m \-XX:MaxMetaspaceSize=512m \-XX:+UseG1GC \-XX:MaxGCPauseMillis=150 \-XX:ParallelGCThreads=4 \-XX:ConcGCThreads=2 \-XX:+HeapDumpOnOutOfMemoryError \-XX:HeapDumpPath=./dumps/ \-XX:+PrintGCDetails \-XX:+PrintGCDateStamps \-Xloggc:./logs/gc.log \-jar app.jar
3.3 生产环境配置
java -Xms4g -Xmx8g \-Xmn2g \-XX:MetaspaceSize=512m \-XX:MaxMetaspaceSize=1g \-XX:+UseG1GC \-XX:MaxGCPauseMillis=100 \-XX:ParallelGCThreads=8 \-XX:ConcGCThreads=4 \-XX:G1ReservePercent=15 \-XX:InitiatingHeapOccupancyPercent=35 \-XX:+HeapDumpOnOutOfMemoryError \-XX:HeapDumpPath=/data/dumps/ \-XX:+PrintGCDetails \-XX:+PrintGCDateStamps \-XX:+UseGCLogFileRotation \-XX:NumberOfGCLogFiles=10 \-XX:GCLogFileSize=10M \-Xloggc:/data/logs/gc.log \-jar app.jar
3.4 Docker容器配置
FROM openjdk:8-jre# 设置JVM参数
ENV JAVA_OPTS="-Xms1g -Xmx2g -XX:+UseG1GC"# 使用容器感知的JVM
ENV JAVA_TOOL_OPTIONS="-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"CMD java $JAVA_OPTS -jar app.jar
四、实战调优示例
4.1 电商交易系统调优案例
场景特点:
- 高并发、低延迟要求
- 内存使用模式:中等对象分配率,长生命周期对象较多
- 峰值 QPS:10,000+
- 服务器配置:16核32G
调优方案:
#!/bin/bash
java -server \# 堆内存设置-Xms16g -Xmx16g \-Xmn8g \-XX:SurvivorRatio=8 \-XX:MaxTenuringThreshold=10 \# G1 收集器调优-XX:+UseG1GC \-XX:MaxGCPauseMillis=100 \-XX:InitiatingHeapOccupancyPercent=40 \-XX:G1HeapRegionSize=16m \-XX:G1ReservePercent=15 \-XX:G1MixedGCCountTarget=16 \# 元空间与栈-XX:MetaspaceSize=512m \-XX:MaxMetaspaceSize=512m \-Xss512k \# 性能优化-XX:+TieredCompilation \-XX:ReservedCodeCacheSize=512m \-XX:+UseStringDeduplication \# 监控与诊断-XX:+HeapDumpOnOutOfMemoryError \-XX:HeapDumpPath=/app/logs/heapdump.hprof \-Xlog:gc=info:file=/app/logs/gc.log:time,uptime,level,tags:filecount=10,filesize=100M \-XX:ErrorFile=/app/logs/hs_err_pid%p.log \# 应用JAR-jar ecommerce-app.jar
4.2 大数据处理应用调优
场景特点:
- 高吞吐量要求,延迟不敏感
- 大内存使用,频繁创建临时对象
- 批量数据处理
调优方案:
#!/bin/bash
java -server \# 大堆内存设置-Xms32g -Xmx32g \-Xmn24g \-XX:SurvivorRatio=6 \# 并行收集器(吞吐量优先)-XX:+UseParallelGC \-XX:+UseParallelOldGC \-XX:ParallelGCThreads=16 \-XX:GCTimeRatio=99 \-XX:MaxGCPauseMillis=500 \# 大对象处理-XX:PretenureSizeThreshold=2m \-XX:+AlwaysPreTouch \# 性能优化-XX:+UseLargePages \-XX:LargePageSizeInBytes=2m \-XX:+UseCompressedOops \-XX:+UseCompressedClassPointers \# 监控配置-Xlog:gc*=info:file=/app/logs/gc.log:time,uptime,level,tags \-XX:NativeMemoryTracking=detail \-jar data-processing-app.jar
五、监控与诊断工具实战
5.1 命令行工具使用示例
# 1. 实时监控 GC 情况
jstat -gc <pid> 1s# 2. 查看堆内存摘要
jmap -heap <pid># 3. 生成堆转储
jmap -dump:live,format=b,file=heap.hprof <pid># 4. 查看类加载统计
jstat -class <pid> 1s# 5. 线程分析
jstack <pid> > thread_dump.txt# 6. 原生内存跟踪
jcmd <pid> VM.native_memory summary# 7. 全面的诊断信息
jcmd <pid> PerfCounter.print
5.2 自动化监控脚本
#!/bin/bash
# monitor_jvm.shPID=$1
LOG_DIR="/app/monitor"
INTERVAL=30while true; doTIMESTAMP=$(date +%Y%m%d_%H%M%S)# GC 统计jstat -gc $PID > $LOG_DIR/gc_$TIMESTAMP.log 2>&1# 线程转储(每5分钟一次)if [ $(($(date +%s) % 300)) -eq 0 ]; thenjstack $PID > $LOG_DIR/thread_dump_$TIMESTAMP.log 2>&1fi# 内存使用ps -p $PID -o pid,ppid,pmem,pcpu,rsz,vsz,comm >> $LOG_DIR/memory_$TIMESTAMP.log 2>&1sleep $INTERVAL
done
