MAT
MAT工具全称为Memory Analyzer Tool,一款详细分析Java堆内存的工具,该工具非常强大,为了使用该工具,我们需要hprof文件.
HPROF文件存储的是特定时间点,java进程的内存快照。有不同的格式来存储这些数据,总的来说包含了快照被触发时java对象和类在heap中的情况。由于快照只是一瞬间的事情,所以heap dump中无法包含一个对象在何时、何地(哪个方法中)被分配这样的信息。
几个关键概念:
- Histogram:列出内存中的对象,对象的个数以及大小
- Dominator Tree:列出最大的对象以及其依赖存活的Object (大小是以Retained Heap为标准排序的)
- Top Consumers : 通过图形列出最大的object
- Duplicate Class:通过MAT自动分析泄漏的原因
- Shallow heap : 对象本身占用内存的大小,不包含其引用的对象。
(常规对象(非数组)的Shallow size有其成员变量的数量和类型决定。数组的shallow size有数组元素的类型(对象类型、基本类型)和数组长度决定. 因为不像c++的对象本身可以存放大量内存,java的对象成员都是些引用。真正的内存都在堆上,看起来是一堆原生的byte[], char[], int[],所以我们如果只看对象本身的内存,那么数量都很小。所以我们看到Histogram图是以Shallow size进行排序的,排在第一位第二位的是byte,char 。) - Retained Heap : 它表示如果一个对象被释放掉,那会因为该对象的释放而减少引用进而被释放的所有的对象(包括被递归释放的)所占用的heap大小。
(于是,如果一个对象的某个成员new了一大块int数组,那这个int数组也可以计算到这个对象中。相对于shallow heap,Retained heap可以更精确的反映一个对象实际占用的大小(因为如果该对象释放,retained heap都可以被释放)。) - outgoing references :表示该对象的出节点(被该对象引用的对象)。
- incoming references :表示该对象的入节点(引用到该对象的对象)。
- GC Root: GC发现通过任何reference chain(引用链)无法访问某个对象的时候,该对象即被回收。所以JVM就是GC Roots。
- Unreachable指的是可以被垃圾回收器回收的对象,但是由于没有GC发生,所以没有释放,这时抓的内存使用中的Unreachable就是这些对象。
1. 预览信息
打开dump 文件,通常我们需要关注一下几个重要信息, 内存占用饼图,Actions部分的Histogram, Top Consumers
.
我们打开Top Consumers,会生成一个报告,我们可以Biggets Objects overview, 能够看到主要内存占用者
Paste_Image.png
点击下面的biggest Objects 可以查看具体的地址。
还有Biggest Top Level Dominator Classes , 可以看到主要占用内存的都是些什么东东。
2. dump分析
2.1 Histogram
MAT中Histogram的主要作用是查看一个instance的数量,一般用来查看自己创建的类的实例的个数。 可以分不同维度来查看对象的Dominator Tree视图,Group by class、Group by class loader、Group by package 和Histogram类似,时间久了,通过多次对比也可以把溢出对象找出来。 Histogram 中可以分Group,Thread 区分信息。 通常为:选中某一项-> show objects and class -> by incoming reference->merge shortest path to gc root -> exclude weadk reference
等流程来查看具体情况。
可以在上面过滤相关包名,查看到具体类型, 关注objects个数, 表示内存dump 中有多少个相关类型对象, 比如不改存在的 对象存在了,或者有的对象内存中有太多的份数, 这样就可以进行一个全面分析。
也可以选择Group by package ,这样方便根据package来进行分析。
Paste_Image.png
也可以选择thread来进行分析, 这样查看占用内存最多的线程,这些线程可能为有内存问题的线程。
点击右键常用的几个选项:
- List Objects -> with incoming references 查看这个对象被哪些外部对象引用
- List Objects-> with outcoming references 查看这个对象持有的外部对象引用
- Path to GC Roots -> exclude ... references 查看这个对象的GC Root,不包含xxx引用,剩下的基本就是强引用了。因为只有强引用一直存在,gc就一直无法回收该对象,从而也就出现内存泄露。
- Merge shortest path to GC root 找到从GC根结点到一个对象或者一组对象的共同路径。从这里可以查看到对象的引用关系。
2.2 Debug Bitmap
图片一直是内存占用的一个大头,也是引起内存泄露,OOM的常客。所以对图片的分析是需要非常了解,这样才能更好的优化项目。*注意:图片在内存中占用的大小:ARGB_8888 类型的图片 为 内存中图片宽度*内存中图片高度4, 此处需要注意原始图片宽高和内存图片宽高不一致,包括拉伸和压缩,尤其是图片位置放错,比如1080p设备,xxxhdpi下面没有图片,会去别的目录下寻找图片,此时将会对图片拉伸。 **
下面我们来看一下图片的处理。通常dump信息中图片表现为两种类型,Bitmap, byte[]。我们需要知道该图片是哪张图片,这样才能好优化相关的图片代码。
-
Bitmap类型
在mat中通常能够看到bitmap类型,占用了大量的内存,如下面这张图片,在内存中占用2M。 我们可以打开,查看mBuffer变量。Paste_Image.png
选中mBuffer-> 右键选中Copy-> 选择Save Value To File -> 生成一个xxx.data 文件。
-
Byte[] 类型
如下,查看byte的 in comming, 即可看到它是一个bitmap,此时如下图,我们可以直接将该byte数据写入xxx.data 文件。Paste_Image.png
下一步是选中对应的bitmap,打开Inspector 窗口,查看bitmap的尺寸,并且使用GIMP工具(可以安装一个,开源的)打开刚才的data文件,图像类型选择RGB Alpha, 宽度和高度填入图像的宽高,打开即可。
Paste_Image.png
Paste_Image.png
2.3 堆对比
通常为了分析内存是否泄露,内存是否持续增长但没有释放等问题,我们需要dump两次来进行内存堆的对比。
打开两个或多个dump文件,打开Navigation History视图,点击Historgam,选择Add to Comp are Basket,最后选中Compare the Result 。
Paste_Image.png
在对比结果中,主要分析类型或者对象的数量是否有变化, 内存是否有变化。
通过以上手段,我们可以定位到大部分内存问题。