Android检测内存泄漏之leakcanary

内存泄漏,memory leak,开发者经常念叨的一个词,稍不留意,就游走在我们的代码中。Andriod开发,内存泄漏的原因有很多,比如activity的context引用,static引用,广播未取消注册,MVP设计时没有detachView,Rx没有取消subscribe订阅,动画处理等。检测的工具也很多。今天总结下,LeakCanary的使用。

看这图,Js接口引用activity泄漏了528kb。

Js接口引用activity泄漏了528kb

一,大话内存泄漏

Java通过垃圾收集器(GC, garbage collection)来自动管理内存。当一个对象不再被使用,就会被自动回收。而“内存泄漏“就是没有成功回收内存的体现。一个对象已经不需要再使用了,但是因为其它的对象持有该对象的引用,导致它的内存不能被回收。“内存泄漏”积累到一定程度,可能导致OOM,所以在写代码的过程中,要注意导致“内存泄漏”的代码写法,提高代码的健壮性。

二,内存泄漏的类型

如果一个对象是可达的有引用的,但实际上它已经没有再使用了,但引用它的对象依然存在,这样的它就是内存泄漏的对象。内存泄漏可分为以下几种类型:

1、静态变量引起的内存泄漏

通常是,一个静态变量,持有对象的应用,对象销毁了,static修饰的变量还在,导致内存无法回收。我之前在BaseActivity中加入实现类到集合中,就造成过内存泄漏。如过静态context一直持有activity的引用,onDestory执行后造成内存泄漏。

1
2
3
4
5
6
7
8
9
10
public class LeakActivity extends Activity {
//静态context
private static Context sContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak);
sContext = this;//赋值后静态context一直持有activity的引用,onDestory执行后造成内存泄漏。
}
}

我遇到的情况,activity当上下文传,js接口引用webview所在的activity,反正不要静态的引用。

2、非静态内部类引起的内存泄漏

如果用android studio开发,写handle发送消息,下面的写法会有黄色警告,因为可能会引发内存泄漏:

1
2
3
4
5
6
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};

为什么呢?非静态内部类会引用外部类对象(Activity),当它使用了 postDelayed 的时候,如果 Activity 已经 finish 了,而这个 handler 仍然引用着这个 Activity 就会致使内存泄漏,因为这个 handler 会在一段时间内继续被 mainLooper 持有,导致引用仍然存在,在这段时间内,如果内存不够使,可能OOM了。

3、资源未关闭引起的内存泄漏

IO流的操作,切记要close关闭流,文件读写,网络访问等都要及时关闭字节流、字符流;注册了广播要在onDestory方法中销毁。使用了BraodcastReceiver、Cursor、Bitmap等资源时,需要及时释放掉,若没有释放,则会引起内存泄漏。这个比较好理解。

三,内存泄漏检测工具

####用Eclipse开发自带的内存检测工具:Heap。
DDMS中的Heap工具用于大致分析是否存在“内存泄漏”,而MAT工具则用于分析“内存泄漏”发生在哪里,MAT工具中,File->Open Heap Dump,可以打开xxx.hprof文件分析。这个我现在不用。不多说。直接看下面介绍的LeakCanary。

四,LeakCanary,内存泄漏精确检测神器。

要精确地追踪到内存泄漏点,强烈推荐使用Square 公司开源的 LeakCanary开源方案,LeakCanary在Application实现类中一行代码,简单暴力侵入性地捕获内存泄漏代码,甚至捕获Android组件的内存泄漏代码。我发现android动画绘制的时候存在内存泄漏的问题。

1,LeakCanary就像金丝雀监视着你的“煤矿”

不用重复造轮子,直接拿来,android studio开撸步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//在module的build.gradle中:
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
//上面是官方推荐写法,其实你要一行也行:
compile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
//然后在你的Application的子类中install:
public class ExampleApplication extends Application{
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}

Good,好了,LeakCanary这是金丝雀会随着你app的运行,自动安装监测,给你通知。方便你查看那个类出现了内存泄漏,甚至告诉你泄漏了多少M。如图:
activity的静态引用造成泄漏

2,查看log日志,分析LeakCanary收集的数据

一般通过它的通知界面就很明了了,如果要详细看看过程,看log部分如下:SplashActivity has leaked!
Splash has leaked

五,总结

遇到内存泄漏不是什么好事,所以平时写代码,留个心眼,以预防为主。一些通用的解决方案:

1,使用Application的context

需要上下文的时候,如果不是非得activity对象,传入Application的context,因为Application的context的生命周期比Activity长,它是app全局的,相当于static的生命周期。

2,static变量不要引用view实例

3,关闭资源,close,unsubscribe,unregister,null记得吃药。


1
欢迎交流,Dusan,杜乾,291902259!OpenDeveloper!