Fork me on GitHub

LeakCanary源码分析

LeakCanary源码分析

基于版本 1.6.3

使用

依赖

1
2
3
4
5
6
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
// Optional, if you use support library fragments:
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
}

初始化

Application 注入 初始化代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ExampleApplication extends Application {

@Override public void onCreate() {
super.onCreate();
//LeakCanary.isInAnalyzerProcess(this)会调用 sInServiceProcess(context, HeapAnalyzerService.class)来判断是否跟HeapAnalyzerService在同一个进程中。
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
}
}

aar 自带 以下不需要开发者编写
HeapAnalyzerService是一个IntentServcie,主要用来分析生成的hprof文件。我们看下HeapAnalyzerService的清单配置:

1
2
3
4
5
<service
android:name=".internal.HeapAnalyzerService"
android:process=":leakcanary"
android:enabled="false"
/>

可以看到,这个服务运行在单独的“:leakcanary”进程中。如果你浏览下LeakCanary的其他android组件(DisplayLeakService,DisplayLeakActivity等)清单配置,会发现它们都是运行在这个进程中的。为什么HeapAnalyzerService要单独放在这个进程?因为内存泄露时,生成的hprof文件很大,解析的时候会耗费大量内存,如果放在app主进程中,可能会导致OOM。

另外,我们也注意到android:enabled="false"这个属性设置,那说明默认情况下,HeapAnalyzerService这个service是不可用的,那它什么时候打开呢? 带着这个疑问我们接着往下看:

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
//com.squareup.leakcanary.internal.LeakCanaryInternals
public static boolean isInServiceProcess(Context context, Class<? extends Service> serviceClass) {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
try {
packageInfo = packageManager.getPackageInfo(context.getPackageName(), GET_SERVICES);
} catch (Exception e) {
CanaryLog.d(e, "Could not get package info for %s", context.getPackageName());
return false;
}
String mainProcess = packageInfo.applicationInfo.processName;

ComponentName component = new ComponentName(context, serviceClass);
ServiceInfo serviceInfo;
try {
serviceInfo = packageManager.getServiceInfo(component, 0);
} catch (PackageManager.NameNotFoundException ignored) {
// Service is disabled.
/****标记1*******.首次运行的时候,HeapAnalyzerService组件没有打开,所以到这里就返回了***/
return false;
}
//HeapAnalyzerService激活后才会有下边的代码
if (serviceInfo.processName.equals(mainProcess)) {/****标记2*******/
CanaryLog.d("Did not expect service %s to run in main process %s", serviceClass, mainProcess);
// Technically we are in the service process, but we're not in the service dedicated process.
return false;
}

int myPid = android.os.Process.myPid();
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.RunningAppProcessInfo myProcess = null;
List<ActivityManager.RunningAppProcessInfo> runningProcesses;
try {
runningProcesses = activityManager.getRunningAppProcesses();
} catch (SecurityException exception) {
// https://github.com/square/leakcanary/issues/948
CanaryLog.d("Could not get running app processes %d", exception);
return false;
}
if (runningProcesses != null) {
for (ActivityManager.RunningAppProcessInfo process : runningProcesses) {
if (process.pid == myPid) {
myProcess = process;
break;
}
}
}
if (myProcess == null) {
CanaryLog.d("Could not find running process for %d", myPid);
return false;
}

return myProcess.processName.equals(serviceInfo.processName);
}

上边我们说过,HeapAnalyzerService组件默认是关闭的,所以一次执行的时候,到代码中1标识的位置就结束了。第二次执行的时候,才会往下走。此外,在2标记的的位置,如果你把HeapAnalyzerService的进程设置为跟主进程一样,LeakCanary依然可以工作,但要小心OOM。再往下就是判断HeapAnalyzerService服务进程是否跟主进程一样。

查看内存泄漏

原理

如何判断内存泄漏

弱引用探测内存泄露
WeakReference(T referent, ReferenceQueue<? super T> q)
referent被gc回收时,会将包裹它的弱引用注册到ReferenceQueue中,在gc后判断ReferenceQueue有没有referent包裹的WeakReference,就可以判断是否被gc正常回收。

LeakCanary.install

1
2
3
4
5
6
7
8
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}

intall方法中,生成了AndroidRefWatcherBuilder对象,设置了一些参数,并最终调用了buildAndInstall()。

AndroidRefWatcherBuilder

参数类型 默认实现 android平台实现类 含义
HeapDump.Listener HeapDump.Listener.NONE ServiceHeapDumpListener 执行分析HeapDump,在android中是在启动DisplayLeakService在一个新的的进程中,执行解析dump
DebuggerControl DebuggerControl.NONE AndroidDebuggerControl 判断进程是否处于debug状态
HeapDumper HeapDumper.NONE AndroidHeapDumper dump heap到文件中
GcTrigger GcTrigger.DEFAULT GcTrigger.DEFAULT 主动调用GC
WatchExecutor WatchExecutor.NONE AndroidWatchExecutor 延迟调用GC的执行器;在android平台是内部有一个HandThread,通过 handler来做延迟操作
HeapDump.Builder HeapDump.Builder HeapDump.Builder 主要是构造执行dump时的一些参数

注意两点:

  • AndroidRefWatcherBuilder与RefWatcherBuilder使用了builder模式,可以看下是怎么用泛型实现Builder模式的继承结构的。
  • 参数里边的默认实现,都是在接口定义中使用匿名内部类定义了一个默认的的实现,这种写法可以参考,比如DebuggerControl的定义
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public interface DebuggerControl {
    DebuggerControl NONE = new DebuggerControl() {
    @Override public boolean isDebuggerAttached() {
    return false;
    }
    };

    boolean isDebuggerAttached();
    }

上边说的HeapDump.Builder(Builder模式),最终会构造一个HeapDump对象,看下他有哪些参数:

参数名称 类型 含义
heapDumpFile File hprof文件路径
referenceKey String 是一个UUID,用来唯一标识一个要监测的对象的
referenceName String 用户给监控对象自己定义的名字
excludedRefs ExcludedRefs 要排除的类集合;因为有些内存泄露,不 是我们的程序导致的,而是系统的bug,这些bug我们无能为力,所以做了这样一个列 表,把这些泄露问题排除掉,不会展示给我们
watchDurationMs long 执行RefWatcher.watch()之后,到最终监测到内 存泄露花费的时间
gcDurationMs long 手动执行gc花费的时间
heapDumpDurationMs long 记录dump内存花费的时间
computeRetainedHeapSize boolean 是否计算持有的堆的大小
reachabilityInspectorClasses List>
  • AndroidRefWatcherBuilder#listenerServiceClass()
1
2
3
4
5
6
7
8
9
/**
* Sets a custom {@link AbstractAnalysisResultService} to listen to analysis results. This
* overrides any call to {@link #heapDumpListener(HeapDump.Listener)}.
*/
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}

ServiceHeapDumpListener:先简单说下这个listsener的作用,当监测到内存泄露时,就会hprof文件回调给这个listener,在回调中它会启启动独立的进程,解析这这个文件,并将解析结果传给AbstractAnalysisResultService的实现类,也即install方法中传入的DisplayLeakServiceDisplayLeakService会弹出系统通知提示发生了内存泄露,这个listener将整个流程串了起来。 DisplayLeakService 最后会讲到!
再往下看最后调用的AndroidRefWatcherBuilder.buildAndInstall()

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
/**
* Creates a {@link RefWatcher} instance and makes it available through {@link
* LeakCanary#installedRefWatcher()}.
*
* Also starts watching activity references if {@link #watchActivities(boolean)} was set to true.
*
* @throws UnsupportedOperationException if called more than once per Android process.
*/
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}

build()是用之前传的参数构造了一个RefWatcher对象返回给我们,检测对象内存泄露的逻辑都是这个类处理的。
接下来异步激活组件DisplayLeakActivity,它是用来展示泄露列表信息的,激活组件调用的是 packageManager.setComponentEnabledSetting() 这个api,它可以实现一些特殊的功能,比如如果有系统权限,可以屏蔽应用开机监听广播,有兴趣的可以google一下。因为这个方法可能会阻塞IPC通信,所以放在了异步里边执行。

  • LeakCanaryInternals.setEnabledAsync()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void setEnabledAsync(Context context, final Class<?> componentClass,
final boolean enabled) {
final Context appContext = context.getApplicationContext();
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override public void run() {
setEnabledBlocking(appContext, componentClass, enabled);
}
});
}
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);
}

激活之后,我们桌面上会多一个图标,为什么会多一个图标?谜底就在他的清单配置中的intent-filter

1
2
3
4
5
6
7
8
9
10
11
12
13
激活之后,我们桌面上会多一个图标,为什么会多一个图标?谜底就在他的清单配置中的intent-filter

<activity
android:theme="@style/leak_canary_LeakCanary.Base"
android:name=".internal.DisplayLeakActivity"
android:process=":leakcanary"
....
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

那具体什么时候触发检测的呢?这就是ActivityRefWatcher和FragmentRefWatcher要做的事儿。对于Activity和Fragment来说,当它的onDestory()被回调之后,就应该被系统回调掉。

ActivityRefWatcher

  • com.squareup.leakcanary.ActivityRefWatcher#install()
1
2
3
4
5
6
7
8
9
10
11
12
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);

application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};

到这里我们就能证实了当Acitivity.onDestroy() 监听内存泄露 refWatcher.watch(activity);

FragmentRefWatcher

  • com.squareup.leakcanary.internal.FragmentRefWatcher.Helper#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
public static void install(Context context, RefWatcher refWatcher) {
List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
if (SDK_INT >= O) {
fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
}
try {
Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
Constructor<?> constructor =
fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
FragmentRefWatcher supportFragmentRefWatcher =
(FragmentRefWatcher) constructor.newInstance(refWatcher);
fragmentRefWatchers.add(supportFragmentRefWatcher);
} catch (Exception ignored) {
}
if (fragmentRefWatchers.size() == 0) {
return;
}
Helper helper = new Helper(fragmentRefWatchers);
Application application = (Application) context.getApplicationContext();
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity);
}
}
};

Fragment的onDestory() 监听的是Acitivity#onDestory() 监听要麻烦一些,我们知道以前不管是原生和support库中的Fragment是没有统一的系统回调的,后来在Support库中和android O以上的版本增加了对fragment的生命周期回调fragmentManager.registerFragmentLifecycleCallbacks()
我们只看下是如何对Support库中的fragment处理的。

  • com.squareup.leakcanary.internal.AndroidOFragmentRefWatcher
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
@RequiresApi(Build.VERSION_CODES.O) //
class AndroidOFragmentRefWatcher implements FragmentRefWatcher {

private final RefWatcher refWatcher;

AndroidOFragmentRefWatcher(RefWatcher refWatcher) {
this.refWatcher = refWatcher;
}

private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {

@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
View view = fragment.getView();
if (view != null) {
refWatcher.watch(view);
}
}

@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
refWatcher.watch(fragment);
}
};

@Override public void watchFragments(Activity activity) {
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
  • com.squareup.leakcanary.internal.SupportFragmentRefWatcher
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
class SupportFragmentRefWatcher implements FragmentRefWatcher {
private final RefWatcher refWatcher;

SupportFragmentRefWatcher(RefWatcher refWatcher) {
this.refWatcher = refWatcher;
}

private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {

@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
View view = fragment.getView();
if (view != null) {
refWatcher.watch(view);
}
}

@Override public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
refWatcher.watch(fragment);
}
};

@Override public void watchFragments(Activity activity) {
if (activity instanceof FragmentActivity) {
FragmentManager supportFragmentManager =
((FragmentActivity) activity).getSupportFragmentManager();
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
}

可以看到,对于Fragment来对,LeakCanary除了监控fragment又没有回收外,还会监测Fragment的view有没有被及时回收。AndroidOFragmentRefWatcher的代码跟上边的类似。
在android O及以上版本是通过AndroidOFragmentRefWatcher来处理,如果用的是最新的support库,是通过SupportFragmentRefWatcher来处理,他们都继承在FragmentRefWatcher。AndroidOFragmentRefWatcherSupportFragmentRefWatcher 代码基本一样为了适配API !

RefWatcher.watch()

从上可以看到,不管是Fragment,Activity还是View最终都调用了同一个RefWatcher.watch()方法,

以Activity:

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
/**
* Watches the provided references and checks if it can be GCed. This method is non blocking,
* the check is done on the {@link WatchExecutor} this {@link RefWatcher} has been constructed
* with.
*
* @param referenceName An logical identifier for the watched object.
*/
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);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}

private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
// com.squareup.leakcanary.AndroidWatchExecutor watchExecutor
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}

KeyedWeakReference 继承自WeakReference,增加的key和referenceName,这个key会同时放入Set集合中,方便gc回收对象后,做对比。
watchExecutor 在Android中实现是AndroidWatchExecutor

1
2
3
4
5
6
7
8
// com.squareup.leakcanary.AndroidWatchExecutor#execute
@Override public void execute(@NonNull Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
postWaitForIdle(retryable, 0);
}
}

waitForIdlepostWaitForIdle 最终还是调用 waitForIdle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
long delayMillis = initialDelayMillis * exponentialBackoffFactor;
// HandlerThread
backgroundHandler.postDelayed(new Runnable() {
@Override public void run() {
Retryable.Result result = retryable.run();
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}

这个类会在主UI线程空闲的时候,向内部的HandlerThread发送一个消息,执行execute方法传给他的Retryable对象,
调用retryable.run(),如果返回RETRY会继续等待下一个主UI线程空闲回调,直至返回为Done,每次重试都会增大间隔时间。

注意:是通过 Looper.myQueue().addIdleHandler()来监听系统主线程空闲的

到这里我们就应该知道ensureGone最开始这个方法被执行的 HandlerThread线程中

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
@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);

//第一次调用如果系统GC 减少引用
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);
// 关键时刻 heapDumper 实现是 com.squareup.leakcanary.AndroidHeapDumper
File heapDumpFile = heapDumper.dumpHeap();
//生成 dump 文件
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
// 将dump文件构造成HeapDump 对象
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
//HeapDump 对象 heapdumpListener对应的是ServiceHeapDumpListener的analyze()
heapdumpListener.analyze(heapDump);
}
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);
}
}

removeWeaklyReachableReferences调用了两次,第一次调用后,如果系统gc正常回收,retainedKeys就不会再包含KeyedWeakReference 的key;如果没有回收,会手动调用gc,再判断,减少误报;如果两次gc后,对象仍然没有被回收,可能就是真的内存泄露了。再调用heapDumper.dumpHeap()得到内存数据。heapDumper对应的是AndroidHeapDumperLeakDirectoryProvider 指定了dumperHeap()的文件夹路径。
heapDumper.dumpHeap();生成文件后,并且生成HeapDump 对象,HeapDump 对象 heapdumpListener对应的是ServiceHeapDumpListener的analyze()

  • com.squareup.leakcanary.AndroidHeapDumper
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
@Override 
protected @NonNull HeapDumper defaultHeapDumper() {
LeakDirectoryProvider leakDirectoryProvider =
LeakCanaryInternals.getLeakDirectoryProvider(context);
return new AndroidHeapDumper(context, leakDirectoryProvider);
}

@Override @Nullable
public File dumpHeap() {
File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();

if (heapDumpFile == RETRY_LATER) {
return RETRY_LATER;
}

FutureResult<Toast> waitingForToast = new FutureResult<>();
showToast(waitingForToast);

if (!waitingForToast.wait(5, SECONDS)) {
CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
return RETRY_LATER;
}

Notification.Builder builder = new Notification.Builder(context)
.setContentTitle(context.getString(R.string.leak_canary_notification_dumping));
Notification notification = LeakCanaryInternals.buildNotification(context, builder);
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
int notificationId = (int) SystemClock.uptimeMillis();
notificationManager.notify(notificationId, notification);

Toast toast = waitingForToast.get();
try {
Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
cancelToast(toast);
notificationManager.cancel(notificationId);
return heapDumpFile;
} catch (Exception e) {
CanaryLog.d(e, "Could not dump heap");
// Abort heap dump
return RETRY_LATER;
}
}

dumpHeap会弹出一个通知,显示Dumping,并调用Debug.dumpHprofData(heapDumpFile.getAbsolutePath())来保存内存,如果异常dumpHprofData异常会重试。

如果dump成功heapDumper.dumpHeap(),会构造HeapDump 对象,ensureGone() --> heapdumpListener.analyze(heapDump); 并传递给heapdumpListener。前边我们说过heapdumpListener对应的是ServiceHeapDumpListener的analyze()

heapdumpListener 是在 com.squareup.leakcanary.LeakCanary#install()中调用 listenerServiceClass,heapDumpListener()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// com.squareup.leakcanary.ServiceHeapDumpListener#analyze
@Override
public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
// com.squareup.leakcanary.internal.HeapAnalyzerService#runAnalysis
public static void runAnalysis(Context context, HeapDump heapDump,
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
setEnabledBlocking(context, HeapAnalyzerService.class, true);
setEnabledBlocking(context, listenerServiceClass, true);
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
ContextCompat.startForegroundService(context, intent);
}

[注意]:DisplayLeakActivity,已经调用pm.setComponentEnabledSetting()方法激活组件了,但listenerServiceClass对应的DisplayLeakServiceHeapAnalyzerService还没有激活,所以在启动HeapAnalyzerService时,要先把这两个组件激活。
HeapAnalyzerService是一个前台IntentService,最终回调到他的onHandleIntentInForeground方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override 
protected void onHandleIntentInForeground(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);

HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
//生成泄露对象引用链
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
// listenerClassName=DisplayLeakService
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}

在onHandleIntentInForeground中根据Intent的参数,构造了heapAnalyzer ,调用heapAnalyzer.checkForLeak()得到泄露对象的引用链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void sendResultToListener(@NonNull Context context,
@NonNull String listenerServiceClassName,
@NonNull HeapDump heapDump,
@NonNull AnalysisResult result) {
Class<?> listenerServiceClass;
// DisplayLeakService
try {
listenerServiceClass = Class.forName(listenerServiceClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Intent intent = new Intent(context, listenerServiceClass);

File analyzedHeapFile = AnalyzedHeap.save(heapDump, result);
if (analyzedHeapFile != null) {
intent.putExtra(ANALYZED_HEAP_PATH_EXTRA, analyzedHeapFile.getAbsolutePath());
}
ContextCompat.startForegroundService(context, intent);
}

返回的结果通过AnalyzedHeap.save(heapDump, result)保存到了一个文件冲,并返回给listenerServiceClassName代表的Sersvice,即DisplayLeakService,这个服务也是一个前台IntentService,最终回调到DisplayLeakService的onHeapAnalyzed方法:

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
public class DisplayLeakService extends AbstractAnalysisResultService {
@Override
protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) {
HeapDump heapDump = analyzedHeap.heapDump;
AnalysisResult result = analyzedHeap.result;

String leakInfo = leakInfo(this, heapDump, result, true);
CanaryLog.d("%s", leakInfo);

heapDump = renameHeapdump(heapDump);
boolean resultSaved = saveResult(heapDump, result);

String contentTitle;
if (resultSaved) {
PendingIntent pendingIntent =
DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
if (result.failure != null) {
contentTitle = getString(R.string.leak_canary_analysis_failed);
} else {
String className = classSimpleName(result.className);
if (result.leakFound) {
if (result.retainedHeapSize == AnalysisResult.RETAINED_HEAP_SKIPPED) {
if (result.excludedLeak) {
contentTitle = getString(R.string.leak_canary_leak_excluded, className);
} else {
contentTitle = getString(R.string.leak_canary_class_has_leaked, className);
}
} else {
String size = formatShortFileSize(this, result.retainedHeapSize);
if (result.excludedLeak) {
contentTitle =
getString(R.string.leak_canary_leak_excluded_retaining, className, size);
} else {
contentTitle =
getString(R.string.leak_canary_class_has_leaked_retaining, className, size);
}
}
} else {
contentTitle = getString(R.string.leak_canary_class_no_leak, className);
}
}
String contentText = getString(R.string.leak_canary_notification_message);
showNotification(pendingIntent, contentTitle, contentText);
} else {
onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
}

afterDefaultHandling(heapDump, result, leakInfo);
}
}

将泄露结果在系统通知栏上显示。

以上,LeakCanary从注册Activity和Fragment的生命周期回调,到onDestory中开始RefWatcher.watch(),然后dumpHeap,再分析内存泄露引用链,最后将结果展示到系统通知栏的整个过程就基本分析完毕了。

总结

总结下看源码过程中学到的知识点:

  • 内存泄露如何判断(WeakReference)
  • Debug.dumpHprofData()获取内存数据,Debug.isDebuggerConnected()判断是否debug
  • setComponentEnabledSetting()动态开关组件
  • Looper.myQueue().addIdleHandler()注册UI线程空闲回调

链接

-------------本文结束感谢您的阅读-------------

本文标题:LeakCanary源码分析

文章作者:zoudong

发布时间:2018年11月28日 - 17:11

最后更新:2019年03月16日 - 17:03

原始链接:http://blog.zoudongq123.cn/2018/11/28/LeakCanary源码分析/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

分享