现象

观察自己机器性能时候发现年老代在缓慢增长, 如在 2016-08-12 10:00:00至11:00:00 图一

年老代在 40MB-60MB之间 到2016-08-13 10:00:00到11:00:00 图二

年老代已经达到将近160MB

现在进行jvmdump分析 dump了两个时间点的内存,并且用较大内存的时间点和较小内存的时间点进行比较,发现如下 图三

这样看不出什么效果,想到应该是我们自己的代码问题,就进行了过滤(此处省略过滤结果图)

可以看到marinFieldEntity类的实例数增加比较多,初步猜测是由marinlog引起的问题 因此在beta上,我这边移除了marinlog的打点,进行了重新的发布

时间点一 10:10分左右cat链接 oldgen大概在40-50MB,登陆机器,首先找到 java 进程

 ps -ef | grep "java"

jmap 相关的进程

sudo jmap -F -histo 11927 > /tmp/jmap0817.txt

引入流量,让old 进行增长

时间点二 14:10分左右的cat链接 oldgen 大概在60MB, 也进行jmap

sudo jmap -F -histo 11927 > /tmp/jmap081714.txt

将两个文件scp下来之后进行比对,观察 从我个人的角度看,大部分数据基本是正常的,年老代也有缓慢增长的情况。这个时候我觉得,这个增长是正常的

观察其它机器,也有缓慢增长的情况,并且会发生old gc

分析

我们知道 JVM 中 会分成 eden区,新new的对象在会放在eden区中 当新的对象放不下的时候,会发生一次Young GC,这个时候会从eden区移入Survivor区(一般eden区是survivor区的8倍,survivor有两块,两块互相的copy) 经过N次(可配置)Young GC之后,依旧存活的对象会移入年老代,年老代也放不下的时候 会触发一次 old gc。

其中,某次从eden移入surivo中的时候,也可能放不下,这个时候会直接移入年老代,如果年老代也放不下就比较恐怖了,会发生promotion failed(触发full gc)

猜测线上的原因是:一开始new 的对象比较大, eden区不足以存放,直接放入了年老代

我们可以得出一些结论:

  • Old Gen可能会一直增长

解决方法

我们可以适当调整大一些eden区,这样new的对象也可以放下,网上也有一套算法,就是让oldgen 达到一定比例的时候 就发生一次oldgc ,而空闲的区域足以放下一块eden+一块survivor 这样不会触发这种情况下的promotion。

总结:

利用图形工具和 jmap命令,对比两次不同的dump文件,可以比较好的判断程序的GC情况,GC调优是一个比较细的活,根据不同的应用会有不同的配置。



Published

17 August 2016