性能优化-内存分析工具Mat

Mat使用

MAT工具可以帮助开发者定位导致内存泄漏的对象,以及发现大的内存对象,然后解决内存泄漏并通过优化内存对象,以达到减少内存消耗的目的。

使用步骤
  1. 在https://eclipse.org/mat/downloads.php下载MAT客户端。
  2. 打开Mat:点击右键打开刚刚下载好的Mat的包路径,在MacOS目录下,使用命令./MemoryAnalyzer -data ./workspace创建工作空间,就能打开Mat图形化界面了。sh脚本如下:
cd /Android_Tools/mat/mat.app/Contents/MacOS
echo open success!
./MemoryAnalyzer -data ./workspace
echo bye!
exit
  1. 从Android Studio进入Profile的Memory视图,选择需要分析的应用进程,对应用进行怀疑有内存问题的操作,结束操作后,主动GC几次,最后export dump文件。
  2. 因为Android Studio保存的是Android Dalvik/ART格式的.hprof文件,所以需要转换成J2SE HPROF格式才能被MAT识别和分析。Android SDK自带了一个转换工具在SDK的platform-tools下,其中转换语句为:
    ./hprof-conv file.hprof converted.hprof
工具视图
  1. Histogram
    列出内存中的所有实例类型对象和其个数以及大小,并在顶部的regex区域支持正则表达式查找。表示一个类创建的实例的个数
  2. Dominator Tree(支配者)
    列出最大的对象及其依赖存活的Object。相比Histogram,能更方便地看出引用关系。如果从GC Root到达对象A的路径上必须经过对象B,那么B就是A的支配者。
  3. Top Consumers
    通过图像列出最大的Object。
  4. Leak Suspects
    通过MAT自动分析内存泄漏的原因和泄漏的一份总体报告。

分析内存最常用的是Histogram和Dominator Tree这两个视图,视图中一共有四列:

  • Class Name:类名。
  • Objects:对象实例个数。
  • Shallow Heap:对象自身占用的内存大小,不包括它引用的对象。非数组的常规对象的Shallow Heap Size由其成员变量的数量和类型决定,数组的Shallow Heap Size由数组元素的类型(对象类型、基本类型)和数组长度决定。真正的内存都在堆上,看起来是一堆原生的byte[]、char[]、int[],对象本身的内存都很小。因此Shallow Heap对分析内存泄漏意义不是很大。
  • Retained Heap:是当前对象大小与当前对象可直接或间接引用到的对象的大小总和,包括被递归释放的。即:Retained Size就是当前对象被GC后,从Heap上总共能释放掉的内存大小。换句话说,Retained Heap是该对象GC之后所能回收到内存的总和。
常用细节

要全面掌握MAT的用法,必须要先了解 隐藏在 MAT 使用中的四大细节,如下所示:

  1. 善于使用 Regex 查找对应泄漏类。
  2. 使用 group by package 查找对应包下的具体类。
  3. 明白 with outgoing references 和 with incoming references 的区别。with outgoing references:它引用了哪些对象。with incoming references:哪些对象引用了它。
  4. 了解 Shallow Heap 和 Retained Heap 的区别。
    Shallow Heap:表示对象自身占用的内存。
    Retained Heap:对象自身占用的内存 + 对象引用的对象所占用的内存。换句话说,Retained Heap是该对象GC之后所能回收到内存的总和。
MAT查找内存泄漏

对于MAT来说,其常规的查找内存泄漏的方式可以细分为如下三步:

  1. 首先,找到当前 Activity,在 Histogram 中选择其 List Objects 中的 with incoming reference(哪些引用引向了我)。
  2. 然后,选择当前的一个 Path to GC Roots/Merge to GC Roots 的 exclude All 弱软虚引用。
  3. 最后,找到的泄漏对象在左下角下会有一个小圆圈。
查找内存泄漏具体位置
常规方式
  1. 按照包名类型分类进行实例筛选或直接使用顶部Regex选取特定实例。
  2. 右击选中被怀疑的实例对象,选择Merge Shortest Paths to GC Root->exclude all phantom/weak/soft etc references。(显示GC Roots最短路径的强引用)
  3. 分析引用链或通过代码逻辑找出原因。
还有一种更快速的方法就是对比泄漏前后的HPROF数据:
  1. 在两个HPROF文件中,把Histogram或者Dominator Tree增加到Compare Basket。
  2. 在Compare Basket中单击 ! ,生成对比结果视图。这样就可以对比相同的对象在不同阶段的对象实例个数和内存占用大小,如明显只需要一个实例的对象,或者不应该增加的对象实例个数却增加了,说明发生了内存泄漏,就需要去代码中定位具体的原因并解决。

需要注意的是,如果目标不太明确,可以直接定位RetainedHeap最大的Object,通过Select incoming references查看引用链,定位到可疑的对象,然后通过Path to GC Roots分析引用链。
此外,我们知道,当Hash集合中过多的对象返回相同的Hash值时,会严重影响性能,这时可以用 Map Collision Ratio 查找导致Hash集合的碰撞率较高的罪魁祸首。

高效方式

在本人平时的项目开发中,一般会使用如下几种方式来快速对指定页面进行内存泄漏的检测(也称为运行时内存分析优化):

  1. shell命令 + LeakCanary + MAT:运行程序,所有功能跑一遍,确保没有改出问题,完全退出程序,手动触发GC,然后使用adb shell dumpsys meminfo packagename -d命令查看退出界面后Objects下的Views和Activities数目是否为0,如果不是则通过LeakCanary检查可能存在内存泄露的地方,最后通过MAT分析,如此反复,改善满意为止。
  2. Profile MEMORY:运行程序,对每一个页面进行内存分析检查。首先,反复打开关闭页面5次,然后收到GC(点击Profile MEMORY左上角的垃圾桶图标),如果此时total内存还没有恢复到之前的数值,则可能发生了内存泄露。此时,再点击Profile MEMORY左上角的垃圾桶图标旁的heap dump按钮查看当前的内存堆栈情况,选择按包名查找,找到当前测试的Activity,如果引用了多个实例,则表明发生了内存泄露。
  3. 从首页开始用依次dump出每个页面的内存快照文件,然后利用MAT的对比功能,找出每个页面相对于上个页面内存里主要增加了哪些东西,做针对性优化。
  4. 利用Android Memory Profiler实时观察进入每个页面后的内存变化情况,然后对产生的内存较大波峰做分析。

此外,除了运行时内存的分析优化,我们还可以对App的静态内存进行分析与优化。静态内存指的是在伴随着App的整个生命周期一直存在的那部分内存,那我们怎么获取这部分内存快照呢?
首先,确保打开每一个主要页面的主要功能,然后回到首页,进开发者选项去打开"不保留后台活动"。然后,将我们的app退到后台,GC,dump出内存快照。最后,我们就可以将对dump出的内存快照进行分析,看看有哪些地方是可以优化的,比如加载的图片、应用中全局的单例数据配置、静态内存与缓存、埋点数据、内存泄漏等等。

Mat使用进阶:
MAT使用进阶

Java内存泄漏分析系列之七:使用MAT的Histogram和Dominator Tree定位溢出源

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值