对于Android性能优化,其中很重要一块就是内存优化,内存优化主要还是解决内存泄漏问题,我一般分为三步进行排查:
1、使用LeakCanary进行初步排查;
2、使用Android studio自带的Android Profiler分析;
3、使用MemoryAnalyzer(MAT)分析;
对于这三步,如果对内存消耗这块没有严格要求,一般使用LeakCarary就可以了。
如果对内存管控比较严格,一般使用Android Profiler也是可以的,如果还不能定位到问题就可以使用MAT了。LeakCanary这里就不做介绍了,使用非常简单。
LeakCanary地址:https://github.com/square/leakcanary
1、获取hprof文件,可以看下:Android studio导出hprof到MAT工具流程
2、进入到Android Profiler分析内存的地方,如下图:
这里先对上面的一些名词做下解释:
Class Name:对象的类型,比如byte[]就是说这里的对象都是byte数组类型;
Allocations:生成对象的数量,这里还是拿byte[]来说,这里的360就是说生成了360个byte[]对象;
Shallow Size:这些对象所占用的内存(不包括引用对象),这里还是拿byte[]来说,这里的25218442就是360个byte[]对象所占的内存,单位是B,也就是25218442/1024/1024M;
Retained Size:这些对象所占用内(包括应用对象),内存释放时,实际释放的大小就是这里的大小;
举个例子可能就更明白了,有两个对象,对象A的大小是20kb,对象B的大小是10kb,对象B是对象A的成员变量,那么:
Class Name:A Allocations:2 Shallow Size:2*20*1024 Retained Size:2*20*1024+2*10*1024
Class Name:B Allocations:1 Shallow Size:10*1024 Retained Size:10*1024
这样看着是不是就更容易理解了。
相对于MAT来说,Android Profiler就是不好对比,个人感觉还是挺好用的,对于一些内部类、单例模式中的静态引用造成的内存泄露,用LeakCanary都可以检查出来,这里就不多说了,这里说个我遇到的内存泄露,是由逻辑处理上引发的内存泄露:在应用中有许多图片,需要预览大图,预览大图时使用的是全屏的dialog(dialog中使用了viewpage展示图片)。后面在做内存分析的时候,发现byte[]这块增长好快,那就点进去看下,如下图:
点击上面的item,下面就会出现相对应的Reference,可以看出这些byte是被bitmap给占用了,展开bitmap的引用链可以查看到bitmap最终是被谁引用了,,这里也可以查看这些byte是被那张图图片给占用了,右键点击如下图:
之后点击Go to Instance就可以查看这张图片了,如果是代码的话,可以点击Jump to Source跳转到对应的代码中,这些功能在MAT中是没有的,这些都可以帮助我们分析是哪引起的内存泄露。经过上面的分析确定是取消dialog展示图片时,并没有使得图片资源得到释放, 按理说,dialog消失了,资源就该释放了,但为什么没有释放,这就得去看看代码了,由于在代码中将这个dialog声明为了全局变量,取消的时候又没有去置null,这就意味着图片是可以寻找到GC Root的(对GC Root的概念这里不做介绍了,感兴趣的可以自己去百度),这就是说图片的资源是不可能被释放的,那要怎么做呢?其实也很简单,在取消dialog后,将dialog置为null就可以了。关于内存的分析,情况不一样,分析起来也是不一样的,像这种可以说是由逻辑引起的,使用LeakCarary是分析不出来的。对于Android Profiler分析内存泄露,如果有怀疑泄露的地方,或是像这样有明显内存增大的地方还是比较好分析的,如果这样还会分析不出来,那就的使用MAT了。
MAT的方便之处在于可以对比两份hprof文件,什么意思呢?在操作之前获取一份hprof文件,这是没有内存泄露的文件,然后不断的去操作,之后回到操作之前的界面,再去获取一份hprof文件,这样就拿到了两份文件,这样就可以去对比这两份文件中生成对象的差异,多出的对象很可能就是发生了内存泄露,这里来看看如何去对比两份文件,关于如何导出hprof文件及使用MAT打开请看:Android studio导出hprof到MAT工具流程
使用MAT打开hprof文件的界面如下:
这里主要说下Histogram和Dominator Tree这两个点,大多数时候用到的也是这两个点:
1、Histogram是针对对象的数量,可以这么理解,一个类可以创建多个对象,这里查看的就是一个类总共创建了多少个对象;
2、Dominator Tree是针对的对象引用关系,以及该类所有实例对象所占用内存的百分比;
这里以Histogram为例点进去看下:
这里以byte[]为例,这里的Objects是1670,就是说内存中byte[]对象的数量是1670,Shallow Heap的大小13568768,这个大小代表的是这1670个byte[]对象所占用的内存,关于Shallow Heap和Retained Heap的意思其实就是上面提到的Shallow Size和Retained Size,这样看着就和Android Profiler是一样的了,接下来就来看看MAT是如何去对比两份hprof文件的,这里是在Histogram界面,然后在下面的Navigation History选中histogram点击右键,在点击Add to Compare Basket,如下图:
这样操作完后以同样的方式打开另一份hprof文件,结果如下图:
这样操作完后在点击上图红框标示的地方,就可以得到对比的结果,如下图:
这个图就是hprof的对比结果,可以看到byte[]类的对象由1670增加到了1687个,所占用的内存由13568768增加到了54004896个byte,如果觉得这样的对比结果不够明显,那么还可以点击上面的红框部分进行计算对比,比如:
对于这样的一份结果,我们应该主要去分析内存泄露比较大的地方,这里占用内存大的地方是byte[],那我们就可以邮件点击做如下操作:
这样就可以得到GC Root的引用,如下图:
但是结果并没有找到GC Root,所以分析到这就跟不下去了,不过到这我们可以结合下Android Profiler,在Android Profiler中是可以找到GC Root,并且可以知道这份byte[]是一份什么样的数据,比如是图片,那么就可以知道这张图片是什么样的,这里针对的是byte[]数据,如果是是我们自己定义的类对象,一般都是可以找到GC Root的,如果是在不行,那就在回过头来结合Android Profiler分析了。
1、对于由于静态内部类、静态引用等引起的内存泄露,这部分内存泄露是比较好分析,直接使用LeakCanary就可以,可以帮助解决绝大部分的内存泄露。
2、如果还需要细致的内存分析,首先使用的应该是Android studio自带的Android Profiler,这个自带的工具也是很强大的,使用的好基本上是可以帮助我们解决内存泄露的。
3、对于使用Android Profiler还是分析不出来,那就在结合MAT工具了,内存泄露基本就没什么问题了。
还有问题的欢迎一起学习讨论!