序
OOM(Out of Memory)是 Android 开发绕不过去的坎,一定会遇到。但是很多时候,报错的地方都并不是发生问题的原因,只是压死骆驼的***一根稻草。
解决 OOM ,涉及到的东西太多了,从原理、分析工具、解决办法很多个方向,所以准备做一个专门解决 OOM 需要用到的知识,集合成一个系列来发布。
就叫它《吊打 OOM》吧,准备慢慢写好好写,希望完结之后,不惧 OOM。
一、前言
OOM(Out of Memory) 大部分原因来自内存泄露,如果有内存泄露导致系统 GC 无法将无用资源回收,就可能导致下次申请内存空间的时候,出现 OOM 的错误。
其实 Android Studio(以下简称 AS)已经自带了一些内存泄露的检查。但是它的功能有限,暂时只能对 Activity 发生的内存泄露进行检查。
下面来了解如何使用 AS 分析 Activity 的内存泄露。
二、Android Monitor
一般用来做内存泄露的检测,会推荐使用 LeakCannary ,它是 Square 提供的一个检测内存泄露的开源库,集成也非常的简单。
有兴趣的可以去 Github 上看它的文档集成使用。
https://github.com/square/leakcanary
不过大多数情况下,我们可能不会去使用 LeakCanary ,毕竟它还是需要集成在项目中。而如果只是为了检测一下是否有 Activity 的内存泄露情况,其实 AS 中已经提供了检测的工具。
AS 本身也为我们提供了非常方便的 Android Monitor 工具,来帮我们监听当前 Debug 情况下,App 运行的情况,其中就包括 Memory 的监控。
Memory 窗口可以完全监听当前正在 Debug 的 App 的内存使用情况。如果在正常操作的时候,看到此处的内存一直在飙高,却没有降低的迹象,你的 App 就可能存在内存泄露的问题。
先来简单介绍一下 Memory 面板,如图中标记的区域。
1、当前监控的终端设备和进程。
2、被检测的设备的内存分配情况。
Allocated:已经被使用的空间。
Free : 当前剩余的空间。
3、Memroy 分析的工具栏,包含一些内存分析相关的操作。
分析内存的工具栏,从左到右依次的作用是:
- Enable:终止检测。
- Initiate GC :手动触发 GC。
- Jump java heap:获取 hprof 分析文件。
- Start Allocation Tracking: 开始分配追踪。
- Memory monitor help:查看帮助文档。
特别提一下,内存分配情况只是标识当前的内存使用情况,如果还需要继续使用内存,是可以继续申请的,直到超过了 Dalvik Heap 分配的***内存数,就会触发 OOM。
今天介绍的方法,主要会使用到『获取 hprof 分析文件的方法』,去获取一个 hprof 文件进行分析,AS 使用这种方式生成的 hprof 文件,并非标准的 hprof 文件,是专门为 AS 分析使用的,所以如果想要交给 MAT 进行分析,还需要使用 hprof-conv 命令进行转换一下。这不是本文的重点,本文介绍的方式只需要使用 AS 进行分析即可。
点击『Jump java heap』按钮之后,会自动生成当前设备当前进行的内存使用报告,我们就在此基础上进行分析。
生成的时间完全视情况而定,可能受当前使用的内存大小和当前使用的设备配置决定。最终会生成一个 .hprof 的文件,文件的命名掺杂了包名和日期时间,能帮我们很好的区分它们。
生成的 .hprof 文件,会自动将其保存在项目的 captures 目录下,同时也可以使用Captures 窗口查看。
在 hprof 的文件分析窗口中,右边会有一个 Analyzer Tasks 子窗口,它就是本文的主角,用来分析 Activity 内存泄露的。
默认情况如上图,点击右上角的『Perform Analysis』按钮,即可开始分析。
它主要是用来检测泄露的 Activity 和 重复的 Strings 字符串。重复的 String 检测,基本上处于无用的状态,没什么实际用处,新建一个 App ,运行起来,它就是会有很多重复的 String ,占用的空间也不大,基本上也无需我们太过关心。
点击『Perform Analysis』按钮,开始尝试检测。
这里主要是使用 Detect Leaked Activitys 的功能,但是我们这是一个空项目,所以也没有什么关于 Activity 内存泄露的问题。
三、Detect Leaked Activitys
既然没有内存泄露的情况,那我们就手动的造一个 Activity 的内存泄露的情况。
我们知道,对于 Activity 而言,如果有内部类,可能会引发内存泄露,下面就是这样一个会引发内存泄露的的例子。
上面创建了一个内部类,并且在 Activity 中,使用一个 static 的 变量去持有它,它不会在 Activity onFinish() 的时候被回收掉,而是一直处于内存中,就会引发内存泄露。
现在,我们打开 ChildActivity 然后关闭它,点击 『Jump Java Heap』按钮生成内存的 hprof 文件。
再继续使用『Perform Analysis』进行 Activitys 的泄露检测。
可以看到,这里已经能检测出来 ChildActivity 存在内存泄露的问题了。
再进行点击可以查看到更信息的情况,来看个完整的效果。
在 Referebce Tree 中,就可以清晰的看到,是 MemoryLeak 对象,造成的内存泄露。
检测到 Activity 的内存泄露问题,我们只需要修复它就可以了。一些常规的编码习惯(Activity 中的内部类,推荐 static 化),确实是可以避免掉这些问题。
【本文为51CTO专栏作者“张旸”的原创稿件,转载请通过微信公众号联系作者获取授权】