工具软件--Eclipse Memory Analyzer tool

时间:2024-03-28 13:53:37

工具软件–Eclipse Memory Analyzer tool

1、前言
在使用阿里云的OSS服务时,服务器内存高居不下,导致服务异常,最终通过Jmap+MAT找到了内存溢出的方法,定位到了问题所在。

整体思路是先用Jmap从生产上dump下来内存快照,然后用Mat分析工具找出导致内存泄漏的代码,最后结合自己的业务代码,分析出导致泄漏的最终原因。

2、安装
安装方式主要有2种:

1、eclipse插件安装很简单,点击help —install new software------add 然后添加新地址,路径为:http://download.eclipse.org/mat/1.3/update-site/
2、软件版的下载路径为:https://www.eclipse.org/downloads/ 下载解压,运行MemoryAnalyzer.exe即可。

3、配置环境参数
安装完成之后,为了更有效率的使用 MAT,我们还需要做一些配置工作。因为通常而言,分析一个堆转储文件需要消耗很多的堆空间,为了保证分析的效率和性能,在有条件的情况下,我们会建议分配给 MAT 尽可能多的内存资源。你可以采用如下两种方式来分配内存更多的内存资源给 MAT。 一种是修改启动参数 MemoryAnalyzer.exe -vmargs -Xmx4g 另一种是编辑文件 MemoryAnalyzer.ini,在里面添加类似信息 -vmargs – Xmx4g。 至此,MAT 就已经成功地安装配置好了,开始进入实战吧。

4、关于shallow size、retained size
Shallow size就是对象本身占用内存的大小,不包含对其他对象的引用,也就是对象头加成员变量(不是成员变量的值)的总和。 Retained size是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和。换句话说,retained size是该对象被GC之后所能回收到内存的总和。为了更好的理解retained size。 如下图,我们把内存中的对象看成下图中的节点,并且对象和对象之间互相引用。这里有一个特殊的节点GC Roots,正解!这就是reference chain的起点。
工具软件--Eclipse Memory Analyzer tool工具软件--Eclipse Memory Analyzer tool
从obj1入手,上图中蓝色节点代表仅仅只有通过obj1才能直接或间接访问的对象。因为可以通过GC Roots访问,所以左图的obj3不是蓝色节点;而在右图却是蓝色,因为它已经被包含在retained集合内。 所以对于左图,obj1的retained size是obj1、obj2、obj4的shallow size总和;右图的retained size是obj1、obj2、obj3、obj4的shallow size总和。obj2的retained size可以通过相同的方式计算。

5、获取dump文件
5.1、【top】 查看CPU等内存使用情况,找到占用内存最大的PID
5.2、【 jmap -dump:format=b,file=fileName PID】
用PID为943执行jmap命令,其中fileName可以任意命名,执行此命令会在本地生成一个fileName的文件,eg:
jmap -dump:format=b,file=jmap_dump.tmp 943

5.3、【tar -zcvf fileName.tar.gz fileName】
我dump的文件有1G多,所以进行压缩然后再下载到本地环境比较快
eg:tar -zcvf jmap_dump.tmp.tar.gz jmap_dump.tmp
6、初始化MAT环境
打开【Eclipse Memory Analyzer】软件
将dump文件解压缩导入:【File->Open Heap Dump】,导入后会看到以下界面,说明dump的文件和软件安装无误。
工具软件--Eclipse Memory Analyzer tool

7、具体分析

7.1、leak suspects分析
文件导入后,点击下图 工具软件--Eclipse Memory Analyzer tool
然后选中:leak suspects,运行完成后,会看到以下界面:
工具软件--Eclipse Memory Analyzer tool
该图深色区域被怀疑有内存泄漏,可以发现整个heap598.3M内存,深色区域就占了531.4M。接下来是一个简短的描述,告诉我们PoolingHttpClientConnectionManager线程占用了大量内存,并建议用关键字"java.lang.Object[]"进行检查。所以,MAT通过简单的两句话就说明了问题所在,就算使用者没什么处理内存问题的经验。在下面还有一个"Details"链接,在点开之前不妨考虑一个问题:为何对象实例会聚集在内存中,为何存活(而未被GC)?是的——Strong Ref,那么再走近一些吧。

7.2、点击 Details 按钮
点击了"Details"链接之后,除了在上一页看到的描述外,还有Shortest Paths To the Accumulation Point和Accumulated Objects部分,这里说明了从GC root到聚集点的最短路径,以及完整的reference chain。
工具软件--Eclipse Memory Analyzer tool
可以看到 ,connectionManagers类里面有 dlementData数组未被释放

7.3、查看Path to GC Roots
选中上图红线部分内容,如下图所示选择【Path to GC Roots->exclude all phantom/weak/soft etc. references】
工具软件--Eclipse Memory Analyzer tool
如图,可看出 IdleConnectionReaper对象属于强引用,无法被回收掉,初步定位是在调用阿里云的方法时出问题,然后再通过以下方法进行进一步确认报错。
工具软件--Eclipse Memory Analyzer tool
8、找出溢出源
8.1、Histogram视图
(截图里柱子那个,边上的是Dominator Tree ):列出每個class产生了多少個实例,以及占有多大内存,所占百分比:
工具软件--Eclipse Memory Analyzer tool
可以很容易找出站内存最多的几个类,根据Retained Heap排序,找出前几个。 可以分不同的维度来查看类的Histogram视图,Group by class、Group by superclass、Group by class loader、Group by package 只要有溢出,时间久了,溢出类的实例数量或者其占有的内存会越来越多,排名也就越来越前,通过多次对比不同时间点下的Histogram图对比就能很容易把溢出类找出来。 工具软件--Eclipse Memory Analyzer tool
由此可看出,com.aliyun.oss.ClientConfiguration 类及下面几个类存在内存溢出
8.2、Dominator Tree支配树
列出每个对象(Object instance)与其引用关系的树状结构,还包含了占有多大内存,所占百分比 可以很容易的找出占用内存最多的几个对象,根据Percentage(百分比)来排序。 可以分不同维度来查看对象的Dominator Tree视图,Group by class、Group by class loader、Group by package 和Histogram类似,时间久了,通过多次对比也可以把溢出对象找出来,Dominator Tree和Histogram的区别是站的角度不一样,Histogram是站在类的角度上去看,Dominator Tree是站的对象实例的角度上看,Dominator Tree可以更方便的看出其引用关系。

工具软件--Eclipse Memory Analyzer tool
8.3、定位溢出的原因
通过Path to GC Roots或者Merge Shortest Paths to GC Roots
工具软件--Eclipse Memory Analyzer tool
通 过Histogram视图或者Dominator Tree视图,找到疑似溢出的对象或者类后,选择Path to GC Roots或者Merge Shortest Paths to GC Roots,这里有很多过滤选项,一般来讲可以选择exclude all plantom/weak/soft etc. references。这样就排除了虚引用、弱引用、以及软引用,剩下的就是强引用。从GC上说,除了强引用外,其他的引用在JVM需要的情况下是都可以 被GC掉的,如果一个对象始终无法被GC,就是因为强引用的存在,从而导致在GC的过程中一直得不到回收,因此就内存溢出了。 接下来就需要直接定位具体的代码,看看如何释放这些不该存在的对象,比如是否被cache住了,还是其他什么原因。 找到原因,清理干净后,再对照之前的操作,看看对象是否还再持续增长,如果不在,那就说明这个溢出点被成功的堵住了。 最后用jstat跟踪一段时间,看看Old和Perm区的内存是否最终稳定在一个范围内,如果长时间稳定在一个范围,那溢出的问题就解决了,如果还再继续增长,那继续用上述方法,看看是否存在其他代码的溢出点,继续找出,将其堵住。

8.4、其他操作
此外通过list objects或show objects by class也可以达到类似的效果,不过没看GC Roots的方式直观,这里就不细说了。

list objects – with outgoing references : 查看这个对象持有的外部对象引用。
list objects – with incoming references : 查看这个对象被哪些外部对象引用。
show objects by class – with outgoing references :查看这个对象类型持有的外部对象引用
show objects by class – with incoming references :查看这个对象类型被哪些外部对象引用

另附一链接:
https://blog.csdn.net/qq_39172525/article/details/80824897
https://www.jianshu.com/p/f8c74943ccd8