简单判断是否有内存泄漏
判断内存泄漏的定位的大单位是Activity。
可以通过反复进入退出一个Activity,然后用adb shell dumpsys meminfo + 包名 查看虚拟机的堆是否有不断地增长
定位内存泄漏
1.使用Leak Canary
在代码上加入Leak Canary,然后不断跑Monkey或者手动反复进出不同页面。若出现内存泄漏问题,会自动导出来,生成以下页面。
2.使用DDMS导出hprof,并用MAT工具进行分析
0)强烈建议先跑30分钟Monkey测试
1)使用eclipse的ddms找到对应的进程,触发一次gc后,dump出里面的内存快照hprof文件以分析当前应用内存的堆有什么东西
2)使用Android SDK 里的platform-tools文件夹的 hprof-conv工具,对刚才 hprof 文件进行转换,以至于 后面MAT工具能正常打开
3)使用MAT打开hprof文件,进入Histogram。输入自己猜测可能泄漏的Activity(项目中Activity不多时,可每个Activity都重复以下3、4、5步骤)
4)右键该其中一项,打开菜单选择list objects ->with incoming refs将列出该类的实例
下图展示了对象间的引用关系。
5)右健Path to GC Roots-->exclue all phantom/weak/soft etc. reference,找出这个实例GC后,还会存在什么对象的引用关系。
下图的情况是:NetworkChangeNotifier类导致BrowserActivity导致内存泄漏
常见导致内存泄漏的几个点
生命周期的原因
比如:Activity中关联了一个生命周期超过Activity的Thread,这个Thread 若持有该Activity的引用,就会导致内存泄漏。
内部类的原因
因为内部类会隐式地持有外部类的引用,若内部类不被释放,外部类也是无法释放。常见的有内部的Listener、Callback、Handler等导致。
情景1:若外部类应该释放的时候,内部类还在执行里面的函数,会导致外部类无法释放。
情景2:若一个异步操作,会回调内部类的Listener、Callback、Handler。当外部类应该释放的时候,但是这个异步操作还存在,而这个异步操作类又持有了Listener、Callback、Handler,导致外部类无法被释放。PS:这个原因也属于生命周期的原因。
静态变量的原因
单例类里包含Activity
静态变量的类里引用到Activity
注册与反注册、打开与关闭没成对出现的原因
比如:注册广播接收器、注册观察者(典型的譬如数据库的监听)等。或者自己写的跟Activity引用有关的clear()函数没有成对出现
解决方法
1)解决内部类的问题(以Handler作为例子)
- onDestroy时候remove所有msgActivity finish后未处理的msg是问题根源,所以清空所有未被执行的msg
mHandler.removeCallbacksAndMessages(null);
PS:比如Listener、Callback等其他内部类的问题,页面退出的时候,应该完成必要的清理操作,比如Cancel 请求
- 使用静态内部类 + weakReference
静态内部类不会保留对外部类的引用,如果一定要引用外部类,使用weakReference
static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
PS:比如Listener、Callback等其他内部类的问题,也可以通过这个方法来解决
2)单例类里面尽量不要传入Activity,最好穿入ApplicationContext。假如传入了Activity,持有的时长也不能大于Activity的生命周期
3)对象的注册与反注册要成对出现
4)不使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存
5)因为View会持有Context,所以注意不要异步引用View,不要让静态对象持有View,不要在集合框架中存储View