Android性能优化系列:内存泄漏分析

前言

由于Android系统中每个APP所占用的内存分为两部分:java内存和native内存。
java申请的内存是在vm的堆内存中,如果超过vm设置的内存限制,则会爆出out of memery的异常。native申请的内存则没有限制,native层受native进程对内存大小的限制。
对于java层内存的跟踪、泄漏检测目前有leakcanary的第三方库比较好用,可以在开发阶段对内存泄漏做很好的检测跟踪,接入、使用都比较简单。
如果想要bigger更高点,可以使用Android studio提供的Memory Profiler工具。在3.2版本中改为了Profile app,点击图标之后,会编译安装app,app启动之后会在底部的工具栏出现一个profiler的图标,即可看到下图:
在这里插入图片描述
其中的memory一栏就是我们今天说的内容。
关于memory界面的介绍可以查看官方文档:
使用 Memory Profiler 查看 Java 堆和内存分配

查看内存分配:

如果阅读了官方文档,就会知道可以在某个时间点的内存分配,具体操作就是在手机上面操作,然后在可疑点直接单机,就可以查看该时间点的内存分配情况。
在这里插入图片描述
如图所示,点击之后,就可以看到如下图所示:
在这里插入图片描述
下半部分就是这个时间点当前内存中的对象数据,可以按类查找,也可以按照报名查找。
如果怀疑某个对象泄漏了,那么在手机上做对应的操作(包括创建对象、释放对象)。
比如怀疑某个fragment对象泄漏了,那我先打开fragment,在关闭fragment.点击gc图标,经过gc之后,查看内存中是否有对象存在。这里以helpFragment为例。经过添加helpFragment,在移除fragment之后,选择一个时间点,查看内存对象分配情况。
找到HelpFragment,单击,在右侧的Instance View的tab中,发现内存中存在3个实例。
在这里插入图片描述
右下的call stack显示的是该对象的创建路径,即对象创建过程的调用栈。
这里说明这里存在了内存泄漏。那么问题来了,我想知道具体是哪些对象引用这些对象,我想查看引用链来解决内存泄漏该怎么办呢。这就不得不说Dump java heap功能。

Dump java heap

官方文档翻译为堆转储,它不仅可以查看内存的对象分配,还能看到对象的引用链,从而确认最终的内存泄漏。
操作方法:在gc之后,选择某一段时间,点击Dump Java heap 在这里插入图片描述
进过几秒之后,再次点击,就会生成如下图:(下图中包含两次dump java heap:灰色透明层,蓝色透明层,这里看的是右边蓝色透明层)
在这里插入图片描述

在右下方的References一栏即可看到该对象被其他对象引用的情况。
注意:这一栏也包括很多该类对象中的内部对象引用该对象的方法,比如将该对象作为参数,传到内部成员对象中,则该内部成员对象也会出现在这一栏。比如HelpCreateVIiewManager这一栏。该manager是在fragment在onViewCreate阶段创建的对象,用来作为该fragment的controller。所以在分析泄漏时,这类可以排除掉。
经过查找,发现是YxSlideHelper类中的对象引用导致了内存泄漏
在这里插入图片描述
右键单击,可以看到两个菜单:Jump to Source 和Go to instance。
JumpTo Source是跳转到具体的引用的对象创建的地方,也就是YxSlideHelper类的vald对象那一行。Go to instance是相当于查看YxSlideHelper的引用链。
点击Jump To Source,跳转到YxSlideHelper类的源码,发现代码如下:
在这里插入图片描述
这里注册了一个FragmentLifecycleCallbacks对象到activity的FragmentManager中。到这里还是没问题,问题是是onFragmentAttached()方法中使用到了添加进来的HelpFragment。根据静态内部类的知识,相当于该FragmentLifecycleCallbacks对象中持有了helpFragment对象,而FragmentLifecycleCallbacks对象被activity中的FragmentManager持有,activity没有销毁的话,自然会导致泄漏。解决方法很简单,在HelpFragment被销毁的时候,调用一次fragmentmanger的unregisterFragmentLifecycleCallbacks()方法即可。
fragmentManager中的registerFragmentLifecycleCallbacks和unregisterFragmentLifecycleCallbacks源码如下:
在这里插入图片描述
引用链:
Activity->fragmentmanager->mLifecycleCallbacks->FragmentLifecycleCallbacks ->fragment.

总结:

这里介绍的是如何使用Android studio提供的profiler App工具,结合一次实际使用,来定位内存泄漏问题。如果有具体的怀疑对象,则会比较方便的定位,如果没有怀疑对象,只能通过多次打开关闭或者借助monkey runner来模拟,查看内存增长情况,gc之后,结合代码,查看内存中遗留的本该被销毁的对象。
注意:一定要多次gc,有些对象引用太多,一次gc之后可能还是会存在。

猜你喜欢

转载自blog.csdn.net/aerfahaidao/article/details/86617666