ReferenceQueue

LeakCanary工具实现的最主要的原理就是利用了Java的ReferenceQueue类特性。

1
2
Object obj = new Object();
Ref ref = new Ref(obj);

上面的代码是一个典型的强引用使用场景,如果 ref 没有被系统GC回收,那 obj这个对象是肯定不会被系统GC回收的。 在Android中的内存泄漏大部分都是上面这种情况导致的,所以为了解决这种情况,我们一般会在Ref类中使用若引用持有obj对象,也就是常见的WeakReference类,使用若引用的好处就是,当ref还没被回收的情况下,如果obj对象占用内存过大,系统GC会直接将obj对象进行回收。当GC回收了obj对象后可能需要通知用户线程进行额外的处理操作,这个时候就需要一个引用队列。ReferenceQueue即这样的一个对象,当一个obj被GC回收后,其相应的包装类,即ref对象会被放入queue中。我们可以从queue中获取到相应的对象信息,同时进行额外的处理。比如反向操作,数据清理等。

LeakCanary实现流程

那么LeakCanary是怎么使用这个特性的呢,LeakCanary检测内存泄漏的一个大致流程如下:

  1. Application中调用LeakCanary.install(this)进行注册
  2. install方法中会注册一个Application.ActivityLifecycleCallbacks,在回调的onActivityDestroyed(Activity activity)方法中检测当前Activity是否被GC回收。
  3. onActivityDestroyed(Activity activity)中检测的方法就是创建一个 Activity的弱引用对象。
  4. 判断ReferenceQueue中是否有这个弱引用对象,如果有则说明当前这个Activity已经被GC回收了。
  5. 如果没有则主动调用一次系统的GC,等待100ms后再查看一次。
  6. 如果ReferenceQueue中依旧没有这个弱引用对象,说明当前Activity没有被系统GC回收。
  7. 没有被系统回收就调用 Debug.dumpHprofData(dumpHproFilePath)方法生成内存使用快照文件。

代码分析

LeakCanary.install(this)

1
2
3
4
5
6
public static RefWatcher install(Application application) {
return refWatcher(application)
.listenerServiceClass(DisplayLeakService.class) //状态栏通知service
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) //过滤一些系统bug
.buildAndInstall(); //真正的install
}

buildAndInstall()

1
2
3
4
5
6
7
8
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build(); //创建RefWatcher类
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context); //设置桌面图标可见
ActivityRefWatcher.install((Application) context, refWatcher); //注册refwatcher
}
return refWatcher;
}

buildAndInstall()方法中主要有三步操作:

  1. 创建RefWatcher

  2. 设置桌面图标可见,就是我们使用LeakCanary的时候桌面多出的那个入口图标,其内部是调用了PackageManagersetComponentEnabledSetting方法设置DisplayLeakActivity.class为入口Activity

    1
    2
    3
    4
    5
    6
    7
    8
    public static void setEnabledBlocking(Context appContext, Class<?> componentClass,
    boolean enabled) {
    ComponentName component = new ComponentName(appContext, componentClass);
    PackageManager packageManager = appContext.getPackageManager();
    int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
    // Blocks on IPC.
    packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
    }
  3. 第三步就开始注册Activity的监听

ActivityRefWatcher.install()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public final class ActivityRefWatcher {

public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}

private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}

@Override public void onActivityStarted(Activity activity) {
}

@Override public void onActivityResumed(Activity activity) {
}

@Override public void onActivityPaused(Activity activity) {
}

@Override public void onActivityStopped(Activity activity) {
}

@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}

@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};

private final Application application;
private final RefWatcher refWatcher;

void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}

public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
}

ActivityRefWatcher类中在Application中注册一个Application.ActivityLifecycleCallbacks,然后在onActivityDestroyed(Activity activity)回调方法中调用refWatcher.watch(activity);开始进行检测。

refWatcher.watch(activity)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void watch(Object watchedReference) {
watch(watchedReference, "");
}

public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key); //生成一个随机数作为Key
//创建一个弱引用对象,持有Activity
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);

ensureGoneAsync(watchStartNanoTime, reference);
}

watch()方法中主要有两步操作:

  1. 生成一个随机数Key并存入一个Set集合中
  2. 创建一个弱引用对象KeyedWeakReference

接下来看ensureGoneAsync()方法

ensureGoneAsync()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}

@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);

removeWeaklyReachableReferences(); //检测对象是否回收

if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) { //如果对象已回收返回完成
return DONE;
}
gcTrigger.runGc(); //对象未回收,主动调用一次GC
removeWeaklyReachableReferences(); //再次检测对象是否回收
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

//创建内存使用快照文件
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);

//分析内存泄漏
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}

private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}

private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}

ensureGoneAsync()会调用ensureGone()方法,在ensureGone()方法中进行下面几步操作:

  1. 调用removeWeaklyReachableReferences()检测弱引用对象是否在ReferenceQueue队列中,如果在说明对象已回收,同时删除Set集合中的Key

  2. 判断Set集合中是否存在Key,若依然存在,说明对象未回收

  3. 调用gcTrigger.runGc();,其内部调用系统GC,并等待100ms

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public interface GcTrigger {

    GcTrigger DEFAULT = new GcTrigger() {
    @Override public void runGc() {
    Runtime.getRuntime().gc();
    enqueueReferences();
    System.runFinalization();
    }

    private void enqueueReferences() {
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    throw new AssertionError();
    }
    }
    };
    void runGc();
    }
  4. 再次调用removeWeaklyReachableReferences()方法进行检测

  5. 若依然未回收对象,则开始生产内存分析文件。

其他

LeakCanary默认分析的是Activity的内存泄漏,如果我们需要对Fragment进行内存泄漏分析,可以自己进行调用。

LeakCanary.install(this)返回的是一个RefWatcher对象,我们可以在Application中定义一个静态常量持有这个对象

1
2
3
4
5
6
7
8
9
10
class BaseApplication : MultiDexApplication() {
companion object{
var refWatcher:RefWatcher? = null
}

override fun onCreate() {
super.onCreate()
refWatcher = LeakCanary.install(this)
}
}

然后在项目的BaseFragmentonDestroy()方法中调用

1
2
3
4
 override fun onDestroy() {
super.onDestroy()
BaseApplication.refWatcher?.watch(this)
}