Android内存优化-MAT使用

前言:

MAT,一款用于查找内存泄漏的工具,或者说是一款内存分析工具,因为它并不能给你很明确的指出哪地方发生了内存泄漏,还是要靠你自己根据去分析xx.hprof文件来查找出项目中内存泄漏的地方。刚开始学习的时候也是在网上找了许多资料,看了之后感觉一脸懵逼,自己摸索了很久之后,并且运用到优化自己的项目上,才决定写一篇关于MAT的博客。主要是很多博客说的都是很笼统,就说了下如何操作,然后立马就上图说哪哪哪发生了内存泄漏。只能说学好如何利用MAT来分析内存,从而找到项目中内存泄漏的代码,这对于你自己优化自己的项目有非常大的帮助。还有一个工具是叫做LeakCanary,这个就非常简单了,堪称傻瓜式内存泄漏分析工具。这个网上有很多介绍,我就不对这个工具进行介绍了。对于我自己而言,分析hprof文件更有兴趣点。
在介绍MAT之前,先简单的介绍下android中的GC机制。
在这先说下Dalvik虚拟机定义了4种类型的GC,分别是:

        GC_FOR_MALLOC: 表示是在堆上分配对象时内存不足触发的GC。

        GC_CONCURRENT: 表示是在已分配内存达到一定量之后触发的GC。

        GC_EXPLICIT: 表示是应用程序调用System.gc、VMRuntime.gc接口或者收到SIGUSR1信号时触发的GC。

        GC_BEFORE_OOM: 表示是在准备抛OOM异常之前进行的最后努力而触发的GC。

接下来说下,GC Roots,他是虚拟机中所有java对象的根引用,而根据java回收机制中的可达性算法,GC不会回收GC Roots以及被他们间接引用的对象。(这个GC Roots会在MAT中运用到,所以我们必须先要了解这个定义)举个列子比如GC Roots->A->B, GC Roots中持有A的引用,而A又持有B的引用,这样A,B就不能被回收了,当A释放了B的引用,这样GC Roots就与B断开了,这样B在一定的时机时就会被GC回收。这也就是我们为什么有时候会使用到弱引用,不清楚的java中的4种引用的可以看我这篇博客:

内存优化-java四种引用,让你能更好的构建你的项目

如果可能会存在内存泄漏,但是A是弱引用的话,那当触发GC时,A被回收,那GC Roots与B的连接也就断了,这样就不会造成B一直存在内存中,这样也就不会造成内存泄漏了。

这里我就没有添加可达性分析的图片了,网上也有很多关于可达性图片,不过我感觉用文字这样叙述的话,更能让你理解。

初步理解的GC机制,会让你更好的分析hprof文件,这样我们使用MAT的时候就不会那样一脸懵逼了。那我们接下来就是学习如何使用MAT这个工具了,回归正题。

MAT的使用:

1.下载地址:mat下载,现在基本都是用AS开发了,如果还是在Eclipse上开发的话,可以直接嵌入MAT插件的。
2.下载之后的操作流程,解压之后点击图片上的红框标识就能开打我们的MAT工具。

下图就是我们进入MAT的整个界面了


点击File->Open Heap Dump.. 来选择我们的hprof文件。
3.如何生成我们的hprof文件。
进入到我们的DDMS界面,选择我们的应用进程,然后点击下图红色框标记的图标,根据你的项目大小会等待一小段时间生成一个hprof文件,但是我们MAT是无法识别这个文件的,要通过hprof-conv命令来进行转换。


通过hprof-conv命令转换操作,这里我把生成的文件保存为a.hprof放到了F盘的prof文件夹下。



这样我们就生成了MAT能识别的b.hprof文件了,接下来就通过MAT打开我们的b.hprof文件,进行我们的内存分析工作,从而找到项目中的内存泄漏。
我这就直接拿我从自己项目中生成的hprof文件来讲解了。先看下MAT导入b.hprof文件之后的界面


它的整块内存图就在上部分区域的原型图显示了,不过这基本没啥用,我们要关系的是我红色框标记出来的两块。
1.Histogram--->列出了内存中每个对象的数量,大小以及名字
2.Dominator Tree ---> 列出了每个对象的大小,以及对象间的引用结构。
这两个功能都能帮我们分析出项目中的内存泄露情况。先谈谈Dominator Tree

Dominator Tree

点击Dominator Tree ,然后通过应用包名来过滤:


先看右上角的两个红框,
Shallow Heap :标识自身内存大小,不包括它引用的对象。
Retained Heap: 当前对象大小以及当前对象可直接或间接引用到的对象大小的总和。

我们这里其实也只需看下Retained Heap,不过了解下这两列所代表的含义还是有必要的。一般情况下,内存越大的就越容易出现内存泄露,可以看到我们每一个对象的右侧,还有一个System Class ,它代表这个对象是由系统管理的并不会造成内存泄露,还有一个地方就是有些对象左下角有个橙色的小圆点,它代表的意思就是这个对象能被我们的GC Roots访问到,刚开始就说过,能被GC Roots访问到的对象都是不能被GC回收的。所以我们可以根据这个来排查内存泄露,但是有一点要注意,发生内存泄露的对象并不一定会被标记显示橙色的点,可以看到第三行的MainActivity就是没有显示橙色点(但是它其实已经发生了内存泄露,通过我排查之后)。

根据内存大小来排查的话,那就从第三行开始了,MainActivity占据着第三位,当然你也可以从第二位开始,第二行表明我们的应用有很大一部分内存都是char文件,但是我这已经排查过第二行了所以在这里就直接拿第三行来说。
右键点击第三行,然后Path To GC Roots---> exclude weak references,这里为什么要排除弱引用,如果不清楚的话可以看我的那篇java4种引用类型就明白了。


上图就是通过exclude weak references得到的视图界面,通过一个个对象去排查,发现在项目中的ExitAppUtils中的这个instance这能被GC Roots访问到,也就是不能被回收,那这里就发生了内存泄露了,所以我直接定位到代码中,查看这块地方.

上图是我已经更改过的代码,从上面代码可以知道,这是一个单例,所以这个类与应用有一样持久的生命周期,然而他的mActivityList又持有了MainActivity的引用,所以造成了MainActivity无法被回收,这样就导致了内存泄露了。所以我把他更换成为了弱引用,这样GC触发的时候 这个也会被回收,这样就不会造成内存泄露了。不过在这里要注意一点,可以与ReferenceQueue队列想结合,这样当activity引用被添加到队列中时,代表这个activity引用要被GC回收了,这样就可以对之进行操作,finish()掉这个activity(因为我这个类的作用就是用来统一管理栈中的activity)。如果没有与队列结合,通过弱引用get()到这个引用的时候,也要判断它是否为空,因为弱引用我们并且确定它是否被GC回收了。(不过图上注释那写错了,别介意 = =)
好了,通过Dominator来查找内存泄漏基本是这个流程了,上面已经把处理的操作说的很详细了,相信应该能懂。接下来就说下Histogram这个功能,如何使用它来排查内存泄漏。

Histogram:

点击Histogram,并还是通过项目的包名来过滤。


上图就是通过点击Histogram,以及通过包名来过滤得到的视图。
可以看到上图多了一个Objects的列,就是前面说的对象的个数。我这里是直接进入到应用的主页面我就点击DUMP HPROF file 来获取hprof文件的,没有对项目进行操作,这样就通过这个Objects列看不出来是否发生了内存泄漏。如果你在网上搜索过MAT使用资料的话,就可以在很多篇文章中看到这样的经典案例:
MainActivity内存泄漏,不回收,然后旋转屏幕,这样反复几次,因为MainActivity一直无法被回收,这样就会导致旋转屏幕的时候MainActivity实例被多次创建。这样在Objects列中就能看到几个对象的情况。
上面这种案例可以自己写个demo测试下,旋转几次之后执行一次GC操作,然后再点击DUMP HPROF file来生成hprof文件,这样就能看到了。
然后我们说下另一种查找内存泄漏的方式,其实和Dominator Tree类似,不过在这之前要说下ListObjects中with outgoing references和with incoming references两个的定义,因为我们等下有一步操作是执行这个的。

with outgoing references: 表示当前这个对象被外部引用
with incoming references: 表示当前对象引用的外部对象
而我们排查内存泄漏,基本上只要点击右键执行ListObjects->with outgoing references,毕竟我们一般情况下是分析这个对象的引用是否一直被外部给持有,这里我们也是通过排查大内存的对象开始的。

到了这个页面,然后我们再通过右键点击-->Path To GC Roots-->exclude weak references,来操作了


这样通过一个个引用去排查,又找到了内存泄漏的地方。这里和Dominator Tree一样我就不重复了。

以上的demo都是操作我自己公司项目排查内存泄漏的实例,学会MAT对于你优化自己的项目而言有非常大的帮助,希望通过这篇博客,你能使用MAT来优化自己公司的项目。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值