应用内存泄露排查

内存泄露

用到神器square/leakcanary

A memory leak detection library for Android and Java.
“A small leak will sink a great ship.” - Benjamin Franklin
千里之堤,毁于蚁穴。 – 《韩非子.喻老》

LeakCanary完整集成

配置build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
apply plugin: 'android-library'

allprojects {
repositories{
jcenter()
}
}

dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
}

配置代码
在你的APPLICATION中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
refWatcher = LeakCanary.install(this, LeakLogService.class,
AndroidExcludedRefs.createAndroidDefaults().build());
} else {
refWatcher = RefWatcher.DISABLED;
}

public static RefWatcher getRefWatcher(Context context) {
ExampleApplication application = (ExampleApplication) context.getApplicationContext();
return application.refWatcher;
}
}

之后在需要检测的地方进行监听即可:

1
2
3
4
5
6
7
8
public abstract class BaseFragment extends Fragment {

@Override public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
}

简易集成

参考GavinCT/SimpleLeakCanary
拷贝库文件到项目下:

1
2
3
4
AndroidWatchExecutor.java
GcTrigger.java
KeyedWeakReference.java
RefWatcher.java

在需要监听的地方添加监听代码:

1
2
3
4
protected void onDestroy() {
super.onDestroy();
getApplicationContext().getRefWatcher().watch(this);
}

排查内存泄露

利用Eclipse的Memory Analyzer(Eclipse Memory Analyzer Open Source Project)可以方便的分析内存泄露原因。

使用Studio帮助我们排查泄露
通过Studio我们可以:

  1. 查看内存堆栈情况
  2. 手动/强制垃圾回收,排除LeakCanary误报
  3. 导出Heap信息(Dump Java Heap)

内存信息导出后,可用MAT打开.hprof文件进行分析,可能需要进行转换:

1
hprof-conv source.hprof dist.hprof

打开QQL查询当前内存信息(快捷键CTRL + ENTER执行QQL语句):

1
select * from instanceof android.app.Activity

查看内存信息,如果有多个相同对象即为可疑泄露对象:
右键 -> Merge Shortest Paths to GC Roots -> exclude all phantom/weak/soft etc. refrences
具体排查类,即可发现泄露对象。

解决内存泄露

针对可疑泄露对象,进行代码排查,即可解决。

避免内存泄露

良好编码习惯!

  1. 匿名内部类
  2. 非静态内部类
  3. 单例对象持有Activity或Context对象
    ETC…

REF

使用 Eclipse Memory Analyzer 进行堆转储文件分析
square/leakcanary
Google IO:Android内存管理主题演讲记录 - 技术小黑屋
Android内存泄漏研究 | Jason’s Blog