第一步,判断是否存在内存泄露

1.使用linux脚本或手动循环打印meminfo

#!/bin/bash
while true;
do
adb shell echo ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------;
adb shell date;
adb shell echo  --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------;
adb -s 192.168.5.45 shell dumpsys meminfo com.xxxx.xxxx; 
sleep 2; 
done

2.操作界面,进入和退出activity操作,当Activities的数量与实际情况不符,就可能发生实例被持有无法释放的情况.
如:ActivityA是主界面,此时dumpsys meminfo只会看到Activities的数量为1.如果此时从ActivityA跳转到ActivityB,dumpsys meminfo 就会看到Activities的数量变为了2.再从ActivityB(生命周期已经走到onDestroy了)退出到ActivityA,如果Activities的数量变成了1说明一切正常,如果不为1,那么ActivityB的实例可能被其他生命周期较长的类持有无法释放.(常见:间接或直接的被单例持有或被静态变量持有.间接:如静态变量保存了一个回调,回调实现方法内部使用了activity或持有activity的类的变量)
如果不断的进入ActivityB再退出,且dumpsys meminfo后发现Activities持续增长,那么说明ActivityB的所有实例均被持有,这是一个更为严重的内存泄露问题.(常见Rxbus,EventBus没有调用unsubscribe或unregister,原因是他们单例内部均有个list或map类似的数据存储直接或间接的保存了所有实例,有的人也会自己写管理所有fragment的栈或队列,但是没有及时清空的情况下,也会出现Activities显示的数量持续增长)
在这里插入图片描述

第二步,获取heap信息

从ActivityB退出到ActivityA后,如果发现meminfo中的Activities数量异常,此时保持在ActivityA的界面上,然后进行下面的操作:
1.使用am dumpheap获取target.hprof文件(自己随便命名一下如target.hprof)
由于本文介绍的是使用eclipse MemoryAnalyzer来进行分析,所以获取原始target.hprof文件后,需要再进行转化才能被该工具正确识别和分析.
使用hprof-conv target.hprof target_mat.hprof其中hprof-conv 工具可以到Android SDK中platform-tools中找到.target_mat.hprof是我们最终要使用的文件.然后就可以使用MemoryAnalyzer打开target_mat.hprof进行内存泄露分析了.

linux脚本示例:
注意:com.xxxx.xxxx为应用包名

#!/bin/bash
# tdz created
#adb shell cd /;
adb shell rm -f /sdcard/target.hprof
adb shell am dumpheap com.xxxx.xxxx /sdcard/target.hprof
sleep 8
datename=$(date +%Y_%m_%d-%H:%M:%S)
echo ------------------$datename---------------------
mkdir /home/tdz/AndroidDoc/$datename 
adb pull /sdcard/target.hprof  /home/tdz/AndroidDoc/$datename/target.hprof
sleep 10
echo "sleep finish,start creating hprof for mat"
cd /home/tdz/AndroidDoc/$datename
hprof-conv  target.hprof target_mat.hprof 
echo  文件名为$datename
read -n1 -p $'\n'"Open File Dir now?[Y/N]"$'\n' answer 
case $answer in
(Y|y)
nemo /home/tdz/AndroidDoc/$datename
;;
(N|n)
esac 
read -n1 -p $'\n'"Open MAT now?[Y/N]"$'\n' answer 
case $answer in
(Y|y)
cd /home/tdz/AndroidLonbonTools/mat
./MemoryAnalyzer
;;
(N|n)
echo "shell exit!!!!"
exit 0
esac 

第三步,MAT快速分析内存泄露

1.打开target_mat.hprof文件,点击OQL
在这里插入图片描述
2.输入查询命令select * from instanceof android.app.Activity 然后按住ctrl+enter
在下面就会列出当前所有没释放的activity实例.
在这里插入图片描述
3.选择本应该被释放但还存在的Activity,在我项目中实际的ViewActivity就相当于假设的ActivityB,从上到下选中一个(由于实例可能在不同的地方被持有,所以需要一个一个的排查,看是不是由于相同问题导致的无法释放,还是存在多种类型的内存泄露)
3.1.选中merge shortest paths to gc roots
3.2. 选择exclude all phantom/weak/soft etc. references
在这里插入图片描述
4.从上往下看就是activity具体被持有的链,这里就是defaultUncaughtHandler持有了MainControl类型的control变量,MainControl持有了MacroFrame类型的frame变量,MacroFrame持有了activity
在这里插入图片描述
5.其他几个ViewActivity实例均需要按照相同方式,查看是否都是由同一种情况导致.对于不同情况需要采取不同的解决方案.

以上就是对简单内存泄露进行分析的流程.也是作者目前经常使用的查找方式.由于经验能力有限,仅供参考,也作为个人笔记,不喜勿喷.

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐