文章目录
提供的功能
1. overview部分
38.1MB,就是reachable部分所占的内存大小,包括多少个classes,多少个Objects,以及多少个class loader
2. unreachable objects Histogram
What are unreachable objects in heap dump?
A heap dump can contain unreachable objects, e.g. objects which should be garbage collected but stay around for various reasons. Usually this is due to optimizations in the garbage collection algorithm. The Memory Analyzer removes these objects by default from the object graph.
因为是unreachable的,所以无法直接查看这些对象的引用
unreachable objects所占内存为1140M,这个就是很大了,其中java.lang.Object[] + char[],就占了600M
查看objects功能上与visualvm对比
在这一点上jvisualvm很直观,其500个实例为一组,翻一翻就能看到了(jvisualvm中看到的内容应该是 reachable+unreachable)
jvisualvm中Object[]实例数为627344
在memory analyzer中
reachable:9355
unreachable:617989
这两个之和,正好等于627344
参考:用mat分析内存dump文件中unreachable objects
"然而因为是unreachable的,无法直接查看这些TreeMap对象的引用。如何才有知道到底谁引用它们呢?在代码里grep了一下,发现自己项目中没有任何地方用到TreeMap,从而判断应该是第三方库用到的
注意:下面(从功能3开始)的这些分析都是针对reachable才可以进行的
3. Histogram视图
该视图以Class类的维度展示每个Class类的实例存在的个数、 占用的 [Shallow内存] 和 [Retained内存] 大小,可以分别排序显示。
从Histogram视图可以看出,哪个Class类的对象实例数量比较多,以及占用的内存比较大
Shallow Heap与Retained Heap的区别
Retained Heap(保留大小):仅能通过obj1才能直接或间接访问的对象的shallow heap总和
shallow heap(浅大小):就是其自身的大小
比如这里obj4的shallow Heap,就是obj4的内存大小
obj3的shallow heap,就是obj3的内存大小
obj2的shallow heap,就是obj2的内存大小,而其retained heap,obj2+obj3+obj4
不过,多数情况下,在Histogram视图看到实例对象数量比较多的类都是一些基础类型,如char[](因为其构成了String)、String、byte[],所以仅从这些是无法判断出具体导致内存泄露的类或者方法的,可以使用 List objects
或 Merge Shortest Paths to GC roots
等功能继续钻取数据。如果Histogram视图展示的数量多的实例对象不是基础类型,是有嫌疑的某个类,如项目代码中的bean类型,那么就要重点关注了。
4. Dominator Tree视图
该视图以实例对象的维度展示当前堆内存中Retained Heap占用最大的对象,以及依赖这些对象存活的对象的树状结构
视图中展示了实例对象名、Shallow Heap大小、Retained Heap大小、以及当前对象的Retained Heap在整个堆中的占比
点开Dominator Tree实例对象左侧的“+”,会展示出下一层(next level),当所有引用了当前实例对象的引用都被清除后,下一层列出的objects就会被垃圾回收
这也阐明了支配的含义:父节点的回收会导致子节点也被回收,即因为父节点的存在使得子节点存活
Dominator Tree支配树可以很方便的找出占用Retained Heap内存最多的几个对象,并表示出某些objects的是因为哪些objects的原因而存活
5. Group分组功能
例如在Histogram视图 或 Dominator Tree视图,选择Group by package,可以更好地查看具体是哪个包里的类占用内存大,也很容易定位到自己的应用程序
6. Thread Overview
在Thread Overview视图可以看到:线程对象/线程栈信息、线程名、Shallow Heap、Retained Heap、类加载器、是否Daemon线程等信息
在分析内存Dump的MAT中还可以看到线程栈信息,这本身就是一个强大的功能,类似于jstack命令的效果
当thread dump无法导出,可以通过这种方式查看线程状态,只是这种方式不像文本文件那样直观
而且还能结合内存Dump分析,看到线程栈帧中的本地变量,在左下方的对象属性区域还能看到本地变量的属性,真的很方便
7. List objects
在 Histogram 或 Dominator Tree视图,想要看某个条目(对象/类)的引用关系图,可以使用 List objects 功能
选择 with outgoing references 或 with incoming references
List objects --> with outgoing references
查看当前对象持有的外部对象引用(在对象关系图中为从当前对象指向外的箭头)
List objects --> with incoming references
查看当前对象被哪些外部对象所引用(在对象关系图中为指向当前对象的箭头)
8. Path To GC Roots
在查询到GC root的路径时,是包含所有引用,还是排除一些类型的引用(如软引用、弱引用、虚引用),从GC角度说,一个对象无法被GC,一定是因为有强引用存在,其它引用类型在GC需要的情况下都是可以被GC掉的,所以可以使用 exclude all phantom/weak/soft etc. references 只查看GC路径上的强引用
从对象到GC Roots的路径
9. Merge Shortest Paths to GC roots
从GC Roots到对象的共同路径
就是第8项:Path To GC Roots,顺序反过来
需要注意的是,Paths to GC roots是针对单个对象的,故在Histogram视图无法使用,因为Histogram视图是针对类的,只能使用Merge shortest Paths to GC roots查询
10. Leak Suspects Report
内存泄漏怀疑,一般在overview中就会直接看一下是否有memory leak的可能
使用MAT分析线上内存泄漏案例
场景:
User服务线上间断发生cpu占用高的问题,在cpu占用高的时间内,机器被hang住了,在prometheus上看就是一个中断,负载均衡只有一台发生了问题,无法响应请求
分析:
发现cpu占用高,发现堆内存也很大,有2G,正常运行情况下应该只有几百M
使用MAT查看hprof
在unreachable object里面,发现有170个User实例
查看Object中的值,发现一个规律,那就是这些用户的cert_no全部为null
原因:
查看错误日志
报错result, returns more than one elements
at org.hibernate.jpa.internal.QueryImpl.getSingleResult(QueryImpl.java:539)
是在进行支付宝小程序联合登录的时候发生的,有一行代码是根据支付宝用户的身份证查询我们系统用户,但是如果支付宝用户的身份证返回为null,这样customUserRepository会去查询bs_custom_user表中所有cert_no为null的用户,而这样的用户在内存中有171万之多
在程序中debug的时候,会发现代码运行在这里的时候明显卡顿了很久,大约20秒,在这个期间就是jpa进行查询把这些对象放到内存中,然后由于一个对象无法接收这171万的用户,导致报错:result returns more than one elements
但这里要非常注意的是:
这171万的对象,放到了内存中,并且是unreachable的了,发生了memory leak内存泄漏
参考:
使用Eclipse Memory Analyzer Tool(MAT)分析线上故障(一) - 视图&功能篇
在"Preferences=>Memory Analyzer"中勾选"Keep Unreachable Objects",删除索引文件Dump同路径下的所有".index",即可看到所有的对象。