JVM 参数调优问题与解决方案集合
一、内存相关问题
问题1:堆内存溢出(OutOfMemoryError: Java heap space)
表现:
java.lang.OutOfMemoryError: Java heap space
可能原因:
- 内存泄漏
- 堆大小设置过小
- 大对象分配过多
解决方案:
# 1. 增加堆内存
-Xms4g -Xmx4g
# 2. 启用堆转储,分析内存泄漏
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dump.hprof
# 3. 查看堆使用情况
jmap -heap <pid>
jstat -gcutil <pid> 1000
# 4. 如果使用G1,调整Region大小
-XX:G1HeapRegionSize=16m
# 5. 分析大对象
jmap -histo:live <pid> | head -20
表现:
java.lang.OutOfMemoryError: Metaspace
解决方案:
# 1. 增加元空间大小
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
# 2. 启用元空间GC日志
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC
# 3. 减少类加载(如使用Tomcat)
-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled
# 4. 检查是否有重复类加载
# 查看加载的类数量
jstat -class <pid>
问题3:直接内存溢出(OutOfMemoryError: Direct buffer memory)
解决方案:
# 1. 增加直接内存大小
-XX:MaxDirectMemorySize=512m
# 2. Netty等框架需要额外配置
-Dio.netty.maxDirectMemory=0 # 使用堆内存
或
-Dio.netty.maxDirectMemory=1024m
# 3. 监控直接内存使用
# 使用NMT(Native Memory Tracking)
-XX:NativeMemoryTracking=detail
jcmd <pid> VM.native_memory detail
二、GC性能问题
问题4:频繁Full GC
表现:应用停顿频繁,响应时间不稳定
检测:
# 查看GC情况
jstat -gc <pid> 1000
# 启用详细GC日志
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime
-Xloggc:/path/to/gc.log
解决方案:
CMS收集器:
# 1. 调整触发阈值
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
# 2. 避免Concurrent Mode Failure
-XX:+CMSScavengeBeforeRemark
-XX:+CMSParallelRemarkEnabled
# 3. 预留更多空间
-XX:CMSInitiatingOccupancyFraction=70
-XX:CMSInitiatingPermOccupancyFraction=90
# 4. 启用压缩减少碎片
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=1
G1收集器:
# 1. 调整混合GC触发阈值
-XX:InitiatingHeapOccupancyPercent=45
# 2. 控制最大停顿时间
-XX:MaxGCPauseMillis=200
# 3. 调整并行GC线程数
-XX:ParallelGCThreads=8
-XX:ConcGCThreads=2
# 4. 调整Region大小
-XX:G1HeapRegionSize=4m
# 5. 避免过早Full GC
-XX:G1ReservePercent=15
问题5:Young GC时间过长
解决方案:
# 1. 调整新生代大小
# 设置新生代固定大小
-Xmn2g
# 或设置新生代比例
-XX:NewRatio=3 # 老年代:新生代=3:1
# 2. 调整Survivor区
-XX:SurvivorRatio=8 # Eden:Survivor=8:1:1
# 3. 调整晋升阈值
-XX:MaxTenuringThreshold=15
-XX:+UseTenuringDistribution
# 4. G1特定优化
-XX:G1NewSizePercent=5
-XX:G1MaxNewSizePercent=60
-XX:G1MixedGCLiveThresholdPercent=85
问题6:GC停顿时间不稳定
解决方案:
# 1. 启用G1并优化
-XX:+UseG1GC
-XX:+UnlockExperimentalVMOptions
-XX:G1HeapRegionSize=4m
-XX:MaxGCPauseMillis=100
-XX:G1NewSizePercent=10
-XX:G1MaxNewSizePercent=60
# 2. 使用ZGC(低延迟)
-XX:+UseZGC
-XX:ConcGCThreads=4
-Xms8g -Xmx8g
# 3. 使用Shenandoah(低延迟)
-XX:+UseShenandoahGC
-XX:ShenandoahGCHeuristics=adaptive
三、CPU性能问题
问题7:CPU使用率过高
检测:
# 1. 查看线程CPU使用
top -H -p <pid>
# 2. 生成线程dump
jstack <pid> > thread_dump.txt
# 3. 使用async-profiler
./profiler.sh -d 60 -f profile.svg <pid>
解决方案:
# 1. 减少GC线程竞争
-XX:ParallelGCThreads=4
-XX:ConcGCThreads=2
# 2. 优化JIT编译
-XX:CompileThreshold=10000
-XX:+TieredCompilation
-XX:CICompilerCount=4
# 3. 减少偏向锁
-XX:-UseBiasedLocking
# 4. 调整偏向锁延迟
-XX:BiasedLockingStartupDelay=0
# 5. 优化自旋锁
-XX:PreBlockSpin=10
问题8:上下文切换过多
解决方案:
# 1. 减少线程数
-XX:ParallelGCThreads=4
-XX:ConcGCThreads=2
-Djava.util.concurrent.ForkJoinPool.common.parallelism=4
# 2. 使用协程(如果使用Loom)
--add-modules jdk.incubator.concurrent
# 3. 调整线程栈大小
-Xss256k
# 4. 优化线程池配置
-Dio.netty.eventLoopThreads=4
四、启动和类加载问题
问题9:应用启动慢
解决方案:
# 1. 启用类数据共享(CDS)
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-Xshare:on
# 2. 调整类元数据
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m
# 3. 预加载类
-XX:+AlwaysPreTouch
# 4. 使用AppCDS
# 生成类列表
java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=classes.lst -jar app.jar
# 生成归档
java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=classes.lst -XX:SharedArchiveFile=app-cds.jsa -jar app.jar
# 使用归档
java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=app-cds.jsa -jar app.jar
问题10:类加载冲突
解决方案:
# 1. 启用类加载日志
-XX:+TraceClassLoading
-XX:+TraceClassUnloading
# 2. 检查重复类
-verbose:class
# 3. 使用自定义类加载器隔离
-Djava.system.class.loader=com.example.CustomClassLoader
# 4. 清理类加载器(Tomcat)
-XX:+UseConcMarkSweepGC
-XX:+CMSClassUnloadingEnabled
-XX:+CMSPermGenSweepingEnabled
五、并发和锁问题
问题11:锁竞争激烈
检测:
# 1. 使用jstack查看锁状态
jstack <pid> | grep -A 10 "BLOCKED"
# 2. 使用JMC飞行记录器
解决方案:
# 1. 减少锁粒度
# 代码层面优化
# 2. 使用无锁数据结构
# 使用ConcurrentHashMap, LongAdder等
# 3. 禁用偏向锁(高竞争场景)
-XX:-UseBiasedLocking
# 4. 调整自旋锁参数
-XX:PreBlockSpin=10
-XX:+UseSpinning
# 5. 启用逃逸分析
-XX:+DoEscapeAnalysis
-XX:+EliminateLocks
问题12:死锁检测
解决方案:
# 1. 启用死锁检测(默认开启)
-XX:+PrintConcurrentLocks
-XX:+PrintGCDetails
# 2. 定期线程dump监控
# 脚本定期执行
while true; do jstack <pid> >> thread_dumps.txt; sleep 60; done
# 3. 使用JConsole或VisualVM监控
六、监控和诊断问题
问题13:如何监控JVM状态
解决方案:
# 1. 启用JMX监控
-Dcom.sun.management.jmxremote.port=9010
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Djava.rmi.server.hostname=<hostname>
# 2. 启用NMT监控
-XX:NativeMemoryTracking=summary
-XX:+PrintNMTStatistics
# 3. 开启Flight Recorder(商业版)
-XX:+FlightRecorder
-XX:StartFlightRecording=duration=60s,filename=myrecording.jfr
# 4. 使用async-profiler
# CPU分析
./profiler.sh -d 30 -e cpu -f cpu_profile.html <pid>
# 内存分析
./profiler.sh -d 30 -e alloc -f alloc_profile.html <pid>
# 锁分析
./profiler.sh -d 30 -e lock -f lock_profile.html <pid>
问题14:如何生成和分析堆转储
解决方案:
# 1. 自动生成堆转储
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dumps
# 2. 手动生成堆转储
jmap -dump:live,format=b,file=heap.hprof <pid>
# 3. 分析工具:
# - Eclipse MAT (Memory Analyzer Tool)
# - VisualVM
# - YourKit
# - JProfiler
# 4. 命令行分析
# 查看对象直方图
jmap -histo <pid> | head -20
# 查看存活对象
jmap -histo:live <pid> | head -20
七、网络和I/O问题
问题15:网络连接泄漏
解决方案:
# 1. 启用资源追踪
-Dsun.net.client.defaultConnectTimeout=5000
-Dsun.net.client.defaultReadTimeout=30000
# 2. 监控文件描述符
# 查看进程打开的文件数
ls -l /proc/<pid>/fd | wc -l
# 3. Netty优化参数
-Dio.netty.allocator.type=pooled
-Dio.netty.noPreferDirect=true
-Dio.netty.leakDetectionLevel=advanced
# 4. Tomcat连接池优化
-DmaxConnections=1000
-DmaxThreads=200
-DacceptCount=100
问题16:文件I/O性能问题
解决方案:
# 1. 调整缓冲区大小
-Dsun.nio.ch.maxUpdateArraySize=1024
-Djava.nio.channels.DefaultThreadPool.initialSize=10
# 2. 使用直接缓冲区
-XX:MaxDirectMemorySize=512m
# 3. NIO优化
-Dsun.rmi.dgc.server.gcInterval=3600000
-Dsun.rmi.dgc.client.gcInterval=3600000
八、容器化环境问题
问题17:容器内内存限制问题
解决方案:
# 1. 使用容器感知的JVM(JDK 8u131+,JDK 10+)
-XX:+UseContainerSupport # 默认启用
# 2. 明确设置内存(不要依赖自动检测)
-Xms512m
-Xmx512m
# 3. 设置元空间大小
-XX:MaxMetaspaceSize=256m
# 4. 设置直接内存
-XX:MaxDirectMemorySize=128m
# 5. 设置CPU限制感知
-XX:ActiveProcessorCount=2
# 6. Kubernetes特定配置
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
问题18:容器内GC问题
解决方案:
# 1. 使用适合容器的GC
# 小内存容器
-XX:+UseSerialGC
# 中等内存容器
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
# 大内存容器
-XX:+UseZGC
-XX:+UseNUMA
# 2. 调整堆外内存
-XX:MaxDirectMemorySize=256m
# 3. 监控容器内存
-XX:+PrintContainerStatistics
九、JVM参数最佳实践模板
生产环境G1配置模板
# 基础配置
-Xms4g -Xmx4g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45
# 内存和区域配置
-XX:G1HeapRegionSize=4m
-XX:G1ReservePercent=15
-XX:G1HeapWastePercent=5
# 并行和并发配置
-XX:ParallelGCThreads=8
-XX:ConcGCThreads=2
-XX:G1MixedGCCountTarget=8
# 监控和日志
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCApplicationConcurrentTime
-Xloggc:/var/log/gc-%t.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=10M
# 错误处理
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/heap-dump.hprof
-XX:ErrorFile=/var/log/hs_err_pid%p.log
低延迟应用ZGC配置模板
# 基础配置(JDK 11+)
-Xms8g -Xmx8g
-XX:+UseZGC
# 低延迟优化
-XX:ConcGCThreads=4
-XX:ParallelGCThreads=8
-XX:ZCollectionInterval=5
# 大页面支持
-XX:+UseLargePages
-XX:+UseTransparentHugePages
# 监控
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+UnlockDiagnosticVMOptions
-XX:+ZStatistics
# 堆外内存
-XX:MaxDirectMemorySize=1g
十、诊断工具速查表
| 问题类型 | 诊断工具 | 关键命令 |
| 内存泄漏 | jmap, MAT | jmap -histo:live <pid> |
| CPU过高 | top, jstack, async-profiler | top -H -p <pid> |
| 锁竞争 | jstack, JMC | jstack <pid> \| grep BLOCKED |
| GC问题 | jstat, GC日志 | jstat -gc <pid> 1000 |
| 类加载 | jstat, -verbose:class | jstat -class <pid> |
| 线程问题 | jstack, JMC | jstack <pid> > thread_dump.txt |
| 堆外内存 | NMT, pmap | jcmd <pid> VM.native_memory |
| I/O问题 | lsof, netstat | lsof -p <pid> |
十一、常见误区和注意事项
误区1:过度调优
问题:过早优化,没有基于监控数据
建议:
- 先监控,后调优
- 一次只调整一个参数
- 使用A/B测试验证效果
误区2:盲目使用大内存
问题:设置-Xmx过大,导致GC停顿时间增加
建议:
- 从小内存开始,逐步增加
- 考虑使用G1的
-XX:MaxGCPauseMillis控制停顿
- 监控GC停顿时间分布
误区3:忽略操作系统限制
问题:忘记设置文件描述符限制、透明大页等
建议:
# 检查系统限制
ulimit -a
# 设置Linux内核参数
echo 'vm.swappiness=10' >> /etc/sysctl.conf
echo 'vm.overcommit_memory=1' >> /etc/sysctl.conf
sysctl -p
误区4:容器环境使用物理机配置
问题:容器内内存限制导致OOM Killer杀死进程
建议:
# 始终设置明确的堆大小
-Xms512m -Xmx512m
# 启用容器支持
-XX:+UseContainerSupport
# 设置CPU限制感知
-XX:ActiveProcessorCount=2
这个集合涵盖了JVM调优的常见问题和解决方案。实际使用时,需要根据具体应用场景和监控数据进行调整。记住黄金法则:监控先行,数据驱动,小步快跑。