记录一次线上OOM调优经历
现状:
k8s 的一个pod 有32G内存,每秒产生新对象的峰值在900Mb ---- 1900Mb(根据jstat计算Eden区获得) 。
修改之前的参数
就一个命令行参数是-Xmx31g;
我修改为:
-Xms:30g
-Xmx:30g
-Xmn:15g
-XX:SurvivorRatio=6
以上目的是为了减少年轻代GC频率(由6秒1次 增加到10+秒一次),让Queue队列中的大对象在to区停留的更长。同时,由于队列的大对象紧到不死,通常存活的对象空间就>to区(s0、s1)空间,被移到了老年代。 不过,结果还是失败了,如下两图:
理论上来说,图1中pod启动后就存在4次fgc,9万秒后进行正经的第一次fgc,居然没有作用,直接OOM。
再改为简单的:
-Xms:30g
-Xmx:30g
其默认O区20G,N区10G。继续观察,这一次fgc成功了,但O区有剩余空间后仍然显示因OOM重启服务。
我只好推测是:虚拟机的问题,parallel gc 在回收时应该stw, 回收后有空间的,jvm那一瞬间却认为没有。
特喵的跟我理论知识冲突了/ 进入知识盲区了,说好的fgc 有效呢,最大=最小堆内存 有助于降低性能消耗……。
在官网VM Options 没有找到Old达到一定70%阈值使用率时,触发1次OOM的参数。 CMS回收器倒是有,可不适合我们高吞吐量的服务器需要。
于是我修改为:
-Xms:30g
-Xmx:15g
作用几乎没有,最多减少因初始512M堆内存 面对大量新对象重启后没几分钟直接OOM的次数。
一次失败的处理,只让我加深了各JVM参数的印象。
后来证明:jvm在fgc后,会增加O区容量,如下图所示。 我之前固定了O区容量大小,它fgc后不能增加容量 就OOM,应该是这个原因了。
2022年1月5日添加:
经过持续的测试,Xmx=28g 可以平稳运行至今。由于看不到dump文件,因而可以由此断定是堆外内存不足了。而pod的OOMKiller是 pod自己的内存不足而重启,而不是Java的OOM。
另外Java的OOM只终止线程运行,不终止Java进程。
添加了一个参数 -XX:AggressiveHeap , 官网是这样描述的:
启用 Java 堆优化。这将根据计算机的配置(RAM 和 CPU)将各种参数设置为最适合具有密集内存分配的长时间运行的作业。默认情况下,该选项处于禁用状态并且未优化堆。
经过运行后,其在新老对比不在是1:2,New大约9G,Old容量 是fgc后剩余不可回收对象的3-8倍,一般是5倍左右。由于Old容量变小了,fgc的频率增加。