JVM 参数调优问题与解决方案集合

JVM 参数调优问题与解决方案集合

一、内存相关问题

问题1:堆内存溢出(OutOfMemoryError: Java heap space)

表现

java.lang.OutOfMemoryError: Java heap space

可能原因

  1. 内存泄漏
  2. 堆大小设置过小
  3. 大对象分配过多

解决方案

# 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

问题2:元空间溢出(OutOfMemoryError: Metaspace)

表现

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, MATjmap -histo:live <pid>
CPU过高top, jstack, async-profilertop -H -p <pid>
锁竞争jstack, JMCjstack <pid> \| grep BLOCKED
GC问题jstat, GC日志jstat -gc <pid> 1000
类加载jstat, -verbose:classjstat -class <pid>
线程问题jstack, JMCjstack <pid> > thread_dump.txt
堆外内存NMT, pmapjcmd <pid> VM.native_memory
I/O问题lsof, netstatlsof -p <pid>

十一、常见误区和注意事项

误区1:过度调优

问题:过早优化,没有基于监控数据

建议

  1. 先监控,后调优
  2. 一次只调整一个参数
  3. 使用A/B测试验证效果

误区2:盲目使用大内存

问题:设置-Xmx过大,导致GC停顿时间增加

建议

  1. 从小内存开始,逐步增加
  2. 考虑使用G1的-XX:MaxGCPauseMillis控制停顿
  3. 监控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调优的常见问题和解决方案。实际使用时,需要根据具体应用场景和监控数据进行调整。记住黄金法则:监控先行,数据驱动,小步快跑

# 主业 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×