最近线上一个处事又呈现了频繁Full GC的环境,导致提供的业务常常超时。问题呈现很是不不变,颠末两周的时候,终于又捕获到了一次Full GC,于是接洽运维做Heap Dump之后,颠末一系列阐明,终于办理问题。这次的问题稍微巨大一点,可是也较量有代表性,用到了VisualVM和MAT两个东西,继承记录如下。
现象
这次利用公司的CAT监控平台看到的内存表示如下:
可以看到,详细表示是:
然后调查某一时刻的JMAP histo的内容如下:
num #instances #bytes class name ---------------------------------------------- 1: 5958517 5840833584 [C 2: 37983 706246056 [B 3: 5960539 190737248 java.lang.String 4: 1522282 126603936 [Ljava.lang.Object; 5: 3692000 88608000 java.lang.Double
可以看到"[C"即"char[]"占用内存到达了5.8G,它正是String的内部布局,换句话说,因为存在了大量的大String导致这个问题。
接洽运维举办了Heap Dump之后,就开始了阐明的进程。因为处事器内存有8G,所以相应的DUMP也有8G,对阐明也造成了一点小坚苦。
下面是一些东西的利用进程,操纵系统是OS X 10.8。
利用VisualVM阐明
首先利用VisualVM对Heap Dump举办阐明。阐明需要较量大的内存,而VisualVM默认的内存是256M,所以需要先配置/Applications/VisualVM.app/Contents/Resources/visualvm/etc/visualvm.conf
中的最大内存量,这里我们配置成了4G-J-Xmx4096m
。
好了,此刻打开dump文件,整个阐明进程共占用内存2G,仍然在可接管范畴。之后阐明内存,可以看到跟之前histo一样的类干系。
差异的是,这时候可以点进去,劳务派遣管理系统,查察详细的工具。这里看到,竟然有几个大的String占用了756M的内存,看来我们找到问题了。
发明一个奇怪的现象:“计较保存巨细”之后,这些String的保存巨细都是0。
保存巨细是什么呢?
它是阐明东西从GC roots开始查找,找到的所有不会接纳的工具,然后凭据引用干系,计较出这个“工具以及它引用的工具”的内存巨细。这里很有趣的是,这些工具的保存巨细都是0。
开始我甚至觉得是东西出了问题,厥后想想,其实谜底很简朴:这些大String是姑且工具,没有什么工具持有它——通过阐明这些String的依赖干系也说明白这一点。这些工具是可以被接纳的,换句话说,并不是有明明的内存泄露。
这个“没有工具持有的String”很有意思,让我想到了,会不会是log的进程中,发生了这个String?因为log的时候,会挪用工具的toString()要领,而一个大工具的toString()大概是很大的。查察了一下String的内容,是[Class[field1=xxx,field2=yyy]]
这种布局,险些可以必定是toString()的功效,那么我们此刻只要查察一下详细的内容,到底是那边太大就可以了。
惋惜的是,VisualVM里我实验了各类要领,都没有步伐Dump出这个工具的数据,甚至还实验了VisualVM提供的OQL。
OQL是一种类SQL的表达式,同时整合了一些Javascript的函数成果,可是它的缺点就是太慢了…最后实验用OQL Dump工具未果。
利用MAT阐明
转而实验一下MAT。Eclipse MAT(Memory Analyzer Tool)是一个强大的内存阐明东西,它可以利便的阐明内存泄露的问题。实际利用之后,确实也感受到比VisualVM越发强大一些。
我们利用MAT打开Dump文件,这也是一个漫长的进程。与VisualVM差异的是,MAT阐明的时候,会在当地发生几个姑且文件生存阐明功效,所以相应的内存占用会小一些(1G阁下),第二次打开也会快许多。
打开完成后,我们看到可以看到几个占用内存较大的工具,这就是最可疑的内存泄漏源。意外的是Size显示的只有1.3GB,并且我们的大String都没在内里。