调优JVM其实就是在理解JVM内存结构以及各种垃圾收集器前提下,结合自己的现有的业务来调整参数,使自己的应用能够正常稳定运行。
一般调优JVM会有几种指标可以参考:“吞吐量”、“停顿时间”和“垃圾回收频率”。
调整JVM设置参数
调整内存区域大小以及相关策略(比如整块堆内存占多少、新生代占多少、老年代占多少、Survivor占多少、晋升老年代的条件等等)。
比如(-Xmx设置堆的最大值、-Xms设置堆的初始值、-Xmn表示年轻代的大小、-XX:SurvivorRatio伊甸区和幸存区的比例等等)。
通常来说,JVM参数会遵循系统推荐:
-XX:NewRatio=2,年轻代:老年代=1:2
-XX:SurvivorRatio=8,eden:survivor=8:1
堆内存设置为物理内存的3/4左右
按经验来说IO密集型的可以稍微把「年轻代」空间加大些,因为大多数对象都是在年轻代就会灭亡。
内存计算密集型的可以稍微把「老年代」空间加大些,对象存活时间会更长些。
最小堆内存和最大堆内存的设置
最小堆内存建议设置为总内存的50%,最大堆内存建议设置为总内存的90%。
在容器化环境中,可以通过以下参数让Java应用感知容器的内存限制,从而根据容器的内存限制自动调整最大堆内存大小:
-XX:+UnlockExperimentalVMOptions
-XX:+UseCGroupMemoryLimitForHeap
-XX:MaxRAMFraction=2
MaxRAMFraction
取不同值时,最大堆内存与容器最大内存限制的比例:
MaxRAMFraction=1:最大堆内存占用容器最大内存限制的1/1
MaxRAMFraction=2:最大堆内存占用容器最大内存限制的1/2
MaxRAMFraction=4:最大堆内存占用容器最大内存限制的1/4
考虑到内存中除了最大堆内存以外,还有方法区、线程栈等需要占用内存,MaxRAMFraction
一般取2比较合适。
如果取值为1,在最大堆内存占满时,可能Java应用占用的总内存会超过容器最大内存限制。
调整JVM核心指标
jvm.gc.time:每分钟的GC耗时在1s以内,500ms以内尤佳
jvm.gc.meantime:每次YGC耗时在100ms以内,50ms以内尤佳
jvm.fullgc.count:FGC最多几小时1次,1天不到1次尤佳
jvm.fullgc.time:每次FGC耗时在1s以内,500ms以内尤佳
收集器使用
收集器配置:
默认情况下,JDK 8在服务端模式下使用
-XX:+UseParallelGC
参数,即使用Parallel Scavenge和Serial Old收集器组合进行内存回收。如果应用需要低延迟,可以考虑使用
-XX:+UseConcMarkSweepGC
参数。这种配置下,使用ParNew来收集新生代内存,使用CMS垃圾回收器收集老年代内存。
选择合适的垃圾回收器
选择合适的垃圾回收器,CMS升级到G1甚至ZGC,以及各个垃圾回收器的各种调优参数。
比如:
-XX:+UseG1GC指定 JVM 使用的垃圾回收器为 G1
-XX:MaxGCPauseMillis设置目标停顿时间
-XX:InitiatingHeapOccupancyPercent当整个堆内存使用达到一定比例,全局并发标记阶段就会被启动
根据GC日志进行具体情况排查
如果频繁出现Minor GC
通常情况下,由于新生代空间较小,Eden 区很快被填满,就会导致频繁 Minor GC,因此可以通过增大新生代空间-Xmn来降低 Minor GC 的频率。
频繁出现Full GC
进一步排查是否存在内存泄漏问题。