G1调优实践日记--G1HeapWastePercent和InitiatingHeapOccupancyPercent的应用

背景

最近有个算文本相似度的需求,当然这算法copy过来没做过什么验证就直接上线了,然后应用程序莫名就开始OOM,然后进程直接被kill掉,当然一开始我没想起来是这段算法代码的锅,我把java_pid18776.hprof文件down下来先用jprofiler研究了一番,居然发现没有什么大对象,然后我以为是其它原因,比如kafka消费量大导致的问题,然后去折腾jvm参数。直接导致我因为没有找到根本原因白白浪费时间。但是有失必有得,这次我也了解了G1的一些有用的调优参数,帮助我后期更了解G1

顺便抱怨一句,下图是jhat和jprofiler加载同一个堆文件,jhat直接告诉了OOM的根本原因,而jprofiler直接没有关键信息,不知道是不是软件bug。
在这里插入图片描述
在这里插入图片描述

G1HeapWastePercent和InitiatingHeapOccupancyPercent

当然,这篇文章的重点是两个参数的介绍。这里推荐先看看oracle对G1的官方说明。理解g1的基本原理,对调优参数的使用会有帮助

Getting Started with the G1 Garbage Collector

G1跟之前的垃圾回收器一样,只是它在老年代回收垃圾时更复杂一点,官方对在老年代的回收整体上称为Concurrent Marking Cycle Phases(并发标记周期阶段),而具体每个阶段做了什么可以详细看官方的说明。
在这里插入图片描述
而 Concurrent Marking Cycle Phases 带来的总结说明文档也有写。大致就是

  • 可以同时回收年轻代和老年代。
  • 在标记阶段是并发执行(没有STW),
  • 重新标记阶段比CMS效率更高。

而今天我们介绍的两个参数就是在调Concurrent Marking Cycle Phases。

在这里插入图片描述

首先第一个是 XX:InitiatingHeapOccupancyPercent

  • -XX:InitiatingHeapOccupancyPercent=45 - Percentage of the (entire) heap occupancy to start a concurrent GC cycle. It is used by G1 to trigger a concurrent GC cycle based on the occupancy of the entire heap, not just one of the generations. A value of 0 denotes ‘do constant GC cycles’. The default value is 45 (i.e., 45% full or occupied).

第一个参数的意思就是当整个堆占用超过某个百分比时,就会触发并发GC周期,这个百分比默认是45%,我的理解来说,如果你的项目没有大的cpu负载压力,可以适当降低这个值,带来的好处就是提前开始Concurrent Marking Cycle Phases ,进一步来说,回收 年轻代 and 老年代 也会提前开始,这样有利于防止年轻代晋升老年代失败(老年代容量不足)而触发Full GC

如果你调整 InitiatingHeapOccupancyPercent 的值比较低,你就能在gc log 看到下列的语句频繁出现,也就说明了Concurrent Marking Cycle Phases 的开始
在这里插入图片描述

第二个是 -XX:G1HeapWastePercent ,通过-XX:G1HeapWastePercent指定,默认值5%,也就是在全局并发标记结束后能够统计出所有可被回收的垃圾占Heap的比例值,如果超过5%,那么就会触发之后的多轮Mixed GC,mixed gc会同时回收年轻代+老年代,而这个参数可以指定mixed gc触发的时机。而且mixed gc 可以在 gc log中清楚的记录下来。这个参数与InitiatingHeapOccupancyPercent 结合使用的话可以提前回收老年代,让老年代提前释放空间。

在这里插入图片描述

结语

事情的最后,我通过jhat才知道 是因为文本相似算法对于长的文本会产生大量的char二维数组,会瞬间让系统奔溃。而这次介绍的参数虽然没有在这次事故中帮上忙,但也有意外的收获 – 我觉得这两个参数在系统应用压力不是很大的情况下,可以提前释放老年代的空间,防止 Evacuation Failure的发生。对gc优化有着积极意义。

2020.12.2 号后续补充

在有一次看到一篇小米的技术文章,我感觉我对那两个调优参数理解有误,特此来记录一下,先上文章地址,写的很好的一篇gc调优技术文章。
小米Talos GC性能调优实践

在文章里,对InitiatingHeapOccupancyPercent的使用是根据堆实际占用率来定的,比如你一个程序的堆实际占用一直是2个g,整个堆你设置了5g,然后InitiatingHeapOccupancyPercent 设置为45 ,那么触发 并发标记周期阶段 的时机就是你的堆实际占用涨到2.25个g才会触发。这样每次收集老年代才能真正省出容量。拿我一个在线上的es来举例:

优化前关键参数如下

-Xms5g
-Xmx5g
-XX:G1HeapWastePercent=10 
-XX:InitiatingHeapOccupancyPercent=30

我们的老年代长期维持在2g左右,那么现在的参数gc表现如何呢?我把gc日志用gceasy网站做了图形化的分析。

可以看到,老年代的实际空间一直是一条平滑的直线,每次只能回收一丁点,回收的效率实在是差。
在这里插入图片描述

young gc 的持续时间也大部分在4s左右
在这里插入图片描述

从gc各阶段发生的次数统计 也能发现gc次数比较多,mixed回收间隔时间平均只有2分钟。
在这里插入图片描述

问题就出在 XX:InitiatingHeapOccupancyPercent 设置太小,之前我喜欢设置小是因为怕老年代早早见顶,而且机器也富有余力,就没在意回收效率,理论上至少要设置 2g/5g = 40%以上,才不会频繁启动 并发标记周期阶段。

随后我重新设置gc参数,将 XX:InitiatingHeapOccupancyPercent提高到50%

-XX:G1HeapWastePercent=10
-XX:InitiatingHeapOccupancyPercent=50

直接上图看效果,老年代不再是一条平滑的曲线,mixgc发生时也能回收一定的内存。

在这里插入图片描述
4s左右的gc次数也大大减少
在这里插入图片描述
gc 并发标记周期阶段 次数也降到了只有十位数,比之前有较大的改观。
在这里插入图片描述

这次调整后,cpu占用也下降了不少,但是这次调整后还有一些4s的young gc存在,我查了下gc 日志,它是 并发标记阶段花费的时间,没有到垃圾回收,没有啥问题。
在这里插入图片描述

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值