本文介绍了一次生产环境的JVM GC相关参数的调优过程,经过参数的调整避免了GC卡顿对JAVA服务成功率的影响html
咱们的Java HTTP服务属于OLTP类型,对成功率和响应时间的要求比较高,在生产环境中出现偶现的成功率忽然降低而后又自动恢复的状况,如图所示:java
JVM和GC相关的参数以下:nginx
-Xmx22528m -Xms22528m -XX:NewRatio=2 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled
总结来讲,因为服务中大量使用了Cache,因此堆大小开到了22G。GC算法使用CMS(UseConcMarkSweepGC),开启了下降标记停顿(CMSParallelRemarkEnabled),设置年轻代为并行收集(UseParNewGC),年轻代和老年代的比例为1:2 (NewRatio=2).算法
JVM GC日志相关的参数以下:服务器
-Xloggc:/data/gc.log -XX:GCLogFileSize=10M -XX:NumberOfGCLogFiles=10 -XX:+UseGCLogFileRotation -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -XX:+DisableExplicitGC -verbose:gc
首先使用jmap查看内存使用状况:并发
jmap -histo:live PID
这个命令把程序中当前的对象按照个数和占用的空间排序之后打印出来。这里没有发现使用异常的对象。oracle
若是Cache内容过多也会致使JVM老年代容易被用满致使频繁GC,所以调出GC日志进行查看,发现每次GC之后内存使用通常是从20G下降到5G左右,所以常驻内存的Cache不是致使GC长时间卡顿的根本缘由。对于GC LOG的查看有多种方式,使用VisualVM比较直观,须要安装VisualGC插件:spa
从图中咱们能够看到伊甸园和老年代的空间分配,因为总体内存是20G,设置 -XX:NewRatio=2 所以老年代是14G,伊甸园+S0+S1=7G.net
若是GC须要处理的内存量比较大,执行的时间也就比较长,STW (Stop the World)时间也就更长。按照这个思路调整CMS启动的时间点,但愿提前GC,也就是让GC变得更加频繁可是指望每次执行的时间较少。添加了下面这两个参数:插件
-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=50
意思是说在Old区使用了50%的时候触发GC。实验后发现GC的频率有所增长,可是每次GC形成的陈功率下降现象并无减弱,所以弃用这两个参数。
若是可以下降老年代GC的频率也能够达到下降GC影响的目的,所以尝试让对象在年轻代内存中进行更长时间的驻留,提高这些对象在年轻代GC时候被销毁的几率。使用参数 -XX:MaxTenuringThreshold=31
调整之后收效不明显。
首先补充一下CMS的相关知识,在CMS整个过程当中有两个步骤是STW的,如图红色部分:
CMS并不是没有暂停,而是用两次短暂停来替代串行标记整理算法的长暂停,它的收集周期是这样:
初始标记(CMS-initial-mark),从root对象开始标记存活的对象
并发标记(CMS-concurrent-mark)
从新标记(CMS-remark),暂停全部应用程序线程,从新标记并发标记阶段遗漏的对象(在并发标记阶段结束后对象状态的更新致使)
并发清除(CMS-concurrent-sweep)
并发重设状态等待下次CMS的触发(CMS-concurrent-reset)。
经过GC日志和成功率降低的时间点进行比对发现并非每一次老年代GC都会致使成功率的降低,可是从中发现了一个规律:
前两次GC CMS-Remark过程在4s左右形成了成功率的降低,可是第三次GC并无对成功率形成明显的影响,CMS-Remark只有0.18s。Java HTTP 服务是经过Nginx进行反向代理的,nginx设置的超时时间是3s,因此若是GC卡顿在3s之内就不会对成功率形成太大的影响。
从GC日志中又发现一个信息:
在文档和相关资料中没有找到蓝色部分的含义,猜想是remark处理的内存量,处理的越多就越慢。添加下面两个参数强制在remark阶段和FULL GC阶段以前先在进行一次年轻代的GC,这样须要进行处理的内存量就不会太多。
-XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark
调优之后效果很明显,下面是两台配置彻底相同的服务器在同一时间段的成功率和响应时间监控图,第一个没有添增强制年轻代GC的参数。
在CMS-remark阶段须要对堆中全部的内存对象进行处理,若是在这个阶段以前强制执行一次年轻代的GC会大量减小remark须要处理的内存数量,进而下降JVM卡顿对成功率的影响
对于Java HTTP服务,JVM的卡顿时间应该小于HTTP客户端的调用超时时间,不然JVM卡顿会对成功率形成影响