android开发游记:性能测试中内存泄露排查方法与防止泄露编码心得

来源:互联网 发布:sql注入防御 编辑:程序博客网 时间:2024/06/06 02:01

android的开发中内存泄露的情况一直让不少开发人员头疼,一些不规范的代码在不经意间就造成了内存泄露的问题,这个问题说大不大说小也不小,然而排查的难度却很大,性能测试只能告诉你app有内存泄露的情况,却并不能告诉你具体在哪。面对这样的描述,开发者很难排查出问题的所在。

今天介绍一个android上的内存泄露排查工具,不同其他工具的是,这个工具是嵌入的app中的成为app的一部分,运行过程中如果发生内存泄露的情况会及时弹出提示,并在app结束时进行泄露情况的统计,方便开发人员找到自己的错误。

先看图:
这里写图片描述
这里写图片描述

使用方法:

工具包下载地址:
http://download.csdn.net/detail/liaoinstan/9199071

1、导入工具jar包,复制activity_statistics.xml到layout目录,并在被测试项目的AndroidManifest.xml中声明StatisticsActivity

<activity       android:name="com.pcmgr.mld.detect.StatisticsActivity"       android:label="性能测试结果" ></activity>

2、在Application的onCreate中,加入如下代码

PcmgrAppMonitorTool.init(this);PcmgrAppMonitorTool.enableActivityMemoryLeakDetect(true);PcmgrAppMonitorTool.enableActivityStartupTimeMonitor(true);PcmgrAppMonitorTool.startActivityMemoryLeakDetect();PcmgrAppMonitorTool.startActivityStartupTimeMonitor();

3、在项目退出的部分添加:PcmgrAppMonitorTool.exit()代码(该函数关闭后台线程,并且生成统计页面)
4、运行工程测试即可
5、查看结果,若有内存泄露,则会在SD卡根目录下生成Activity名_Leak.hprof文件,拿到该文件就能定位到具体的内存泄露位置

Demo示例
新建一个测试工程,提供三个Activity,MainActivity、LeakActivity(有内存泄露的Activity)、NoLeakActivity(无内存泄露的Activity)

这里写图片描述

其中LeakActivity内存泄露处如下

这里写图片描述

在主界面启动其余两个页面后,再按返回键返回到主界面

这里写图片描述

执行监控后,结果如下

这里写图片描述

缺点

只能定位到内存泄露的Activity,不能定位到泄露的具体代码行

内存泄露是一种比较隐性的bug,因为不能精确定位问题,所以排查的难度比较高,但同时开发人员也应该提高自己编码的规范,避免发生内存泄露,一般而言比较明显的泄露情况都是由于某个生命周期比activity长的对象得到了这个activity的强引用(例如你把一个TextView放在application中),这样在activity结束的时候由于有强应用指向它而导致整个activity无法正常释放。尤其在application中的变量,如果拥有了某个activity,那么这个activity就无法释放,application的生命周期是从应用开始到应用结束远远长与activity的生命周期,当activity结束的时候application并没有结束而且还持有activity的引用,那么泄露就发生了。同样导致泄露的情况诸如:dialog没有dismiss就结束activity,handler线程没有结束就先结束activity……
内存泄露没有特定的排查方法,但是可以通过提高编码规范来预防泄露的发生,使用上面的工具也能及时的发现泄露的情况这样把泄露的根源扼杀在摇篮里减少错误的发生。

上面是对工具的使用,下面对原理进行一些介绍:

工具实现原理

1、统计activity启动时间实现原理
一般的Activity启动是指从onCreate回调到onResume回调消耗的时间,传统的统计Activity启动时间的实现方式有两种:
一种是写一个BaseActivity,然后在onCreate里面记录启动时间,在onResume里面记录消耗的时间,然后所有的Activity都继承自这个BaseActivity
另一种是直接在onCreate里面记录启动时间,在onResume里面记录消耗的时间
这两种方式普遍的问题是比较繁琐

那有没有一种更简便的方式来统计
android开发者都知道,每一个应用进程都有一个ActivityThread,我们启动Activity时,是通过ActivityThread来启动的,下面以android4.4版本的源码来说明
函数调用链为:ActivityThread.attach->ActivityThread.queueOrSendMessage ->Hanlder.handleMessage->handleLaunchActivity->performLaunchActivity ->Instrumentation.callActivityOnCreate

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

通过源码可以看到,ActivityThread最终是调用Instrumentation的函数来启动Activity(包括Activity其他生命周期回调也是调用Instrumentation的函数),那能否通过实现自己的Instrumentation来替换系统的Instrumentation来实现生命周期的监控
经过反复的尝试,答案是可行的!至此实现的思路如下:
实现一个Instrumentation,并通过反射的方式替换系统的Instrumentation
在自己实现的Instrumentation提供各个生命周期的回调,使用者根据业务逻辑实现对应的回调即可

这样无论工程有多少Activity,均能统计,并且不需要增加任何代码

2、内存泄露检测原理
Android中常用的引用类型有三种:
强引用(StrongReference )
弱引用(WeakReference )
软引用(SoftReference )
其中普通的引用均为强引用,当一个对象未使用时,哪怕有弱引用指向对象,GC也会立即回收该对象,而弱引用与软引用的区别如下

这里写图片描述

实现思路如下:

后台启动一个检测线程,设置检测间隔时间和阀值,利用弱引用的特性,结合前面Hook Instrumentation,当切换Activity时,在onDestroy执行前,使用弱引用保存该Activity对象
每隔一段时间执行System.gc并判断该弱引用保存的对象是否存在,当对象存在并超过阀值时,则判断为内存泄露

1 0