新乡网站建设报价,做百度网站费用多少,网站如何做后台,企业建设网转载自 jvm系列(六):Java服务GC参数调优案例本文介绍了一次生产环境的JVM GC相关参数的调优过程#xff0c;通过参数的调整避免了GC卡顿对JAVA服务成功率的影响。
这段时间在整理jvm系列的文章#xff0c;无意中发现本文#xff0c;作者思路清晰通过步步分析最终解决问题。我…转载自 jvm系列(六):Java服务GC参数调优案例本文介绍了一次生产环境的JVM GC相关参数的调优过程通过参数的调整避免了GC卡顿对JAVA服务成功率的影响。
这段时间在整理jvm系列的文章无意中发现本文作者思路清晰通过步步分析最终解决问题。我个人特别喜欢这种实战类的内容经原作者的授权同意将文章分享于此。备注部分为本人添加主要起到说明的作用。
背景以及遇到的问题
我们的Java HTTP服务属于OLTP类型对成功率和响应时间的要求比较高在生产环境中出现偶现的成功率突然下降然后又自动恢复的情况如图所示JVM和GC相关的参数如下
-Xmx22528m
-Xms22528m
-XX:NewRatio2
-XX:UseConcMarkSweepGC
-XX:UseParNewGC
-XX:CMSParallelRemarkEnabled
总结来说由于服务中大量使用了Cache所以堆大小开到了22G。GC算法使用CMSUseConcMarkSweepGC开启了降低标记停顿CMSParallelRemarkEnabled设置年轻代为并行收集UseParNewGC年轻代和老年代的比例为1:2 NewRatio2.
JVM GC日志相关的参数如下
-Xloggc:/data/gc.log
-XX:GCLogFileSize10M
-XX:NumberOfGCLogFiles10
-XX:UseGCLogFileRotation
-XX:PrintGCDateStamps
-XX:PrintGCTimeStamps
-XX:PrintGCDetails
-XX:DisableExplicitGC
-verbose:gc
问题解决过程
排除应用程序的内存使用问题
首先使用jmap查看内存使用情况jmap -histo:live PID
这个命令把程序中当前的对象按照个数和占用的空间排序以后打印出来。这里没有发现使用异常的对象。
排除Cache内容过多的问题
如果Cache内容过多也会导致JVM老年代容易被用满导致频繁GC因此调出GC日志进行查看发现每次GC以后内存使用一般是从20G降低到5G左右因此常驻内存的Cache不是导致GC长时间卡顿的根本原因。对于GC LOG的查看有多种方式使用VisualVM比较直观需要使用VisualGC从图中我们可以看到伊甸园和老年代的空间分配由于整体内存是20G设置 -XX:NewRatio2 因此老年代是14G伊甸园S0S17G
调整GC时间点成功率抖动问题加重
如果GC需要处理的内存量比较大执行的时间也就比较长STW Stop the World时间也就更长。按照这个思路调整CMS启动的时间点希望提早GC也就是让GC变得更加频繁但是期望每次执行的时间较少。添加了下面这两个参数
-XX:UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction50
意思是说在Old区使用了50%的时候触发GC。实验后发现GC的频率有所增加但是每次GC造成的陈功率降低现象并没有减弱因此弃用这两个参数。
调整对象在年轻代内存中驻留的时间效果不明显
如果能够降低老年代GC的频率也可以达到降低GC影响的目的因此尝试让对象在年轻代内存中进行更长时间的驻留提升这些对象在年轻代GC时候被销毁的概率。使用参数 -XX:MaxTenuringThreshold31调整以后收效不明显。
备注1、MaxTenuringThreshold 在1.5.005之前最大值可以设置为31 1.5.006以后最大值可以设置为15超过15会被认为无限大。2、提升年轻代GC被销毁的概率只是调整这个参数效果不大第二次age的值会重新计算。CMS-Remark之前强制进行年轻代的GC
首先补充一下CMS的相关知识在CMS整个过程中有两个步骤是STW的如图红色部分CMS并非没有暂停而是用两次短暂停来替代串行标记整理算法的长暂停它的收集周期是这样
1、初始标记(CMS-initial-mark),从root对象开始标记存活的对象2、并发标记(CMS-concurrent-mark)3、重新标记(CMS-remark),暂停所有应用程序线程重新标记并发标记阶段遗漏的对象在并发标记阶段结束后对象状态的更新导致4、并发清除(CMS-concurrent-sweep)5、并发重设状态等待下次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
备注1、蓝色部分的含义remark标记需要清理对象的容量。2、FULL GC阶段之前先在进行一次年轻代的GC的意义是Yong区对象引用了Old区的对象如果在Old区进行清理之前不进行Yong区清理就会导致Old区被Yong区引用的对象无法释放。调优以后效果很明显下面是两台配置完全相同的服务器在同一时间段的成功率和响应时间监控图第一个没有添加强制年轻代GC的参数。结论
1、在CMS-remark阶段需要对堆中所有的内存对象进行处理如果在这个阶段之前强制执行一次年轻代的GC会大量减少remark需要处理的内存数量进而降低JVM卡顿对成功率的影响。2、对于Java HTTP服务JVM的卡顿时间应该小于HTTP客户端的调用超时时间否则JVM卡顿会对成功率造成影响。