大致流程:
// Leakcanary的入口函数,
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
上述入口函数中主要做了这几件事:
- 创建AndroidRefWatcherBuilder对象,用于构建RefWatcher对象实例
- 设置内存分析结果的监听DisplayLeakService,该对象主要用于展示日志和显示泄漏信息的通知
- 设置不需要不需要考虑的引用
- 创建RefWatcher对象实例,
这里主要看一下buildAndInstall方法的实现,该方法主要作用是创建一个RefWatcher对象实例,然后开始观察目标对象引用(所谓的目标对象引用具体是指什么呢?)
public RefWatcher buildAndInstall() {
//创建RefWatcher实例
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
//设置用于展示内存泄漏信息的详情的页面可用
LeakCanary.enableDisplayLeakActivity(context);
//安装activity的引用监听,这里可以从命名猜测其观察的目标对象就是activity
ActivityRefWatcher.install((Application) context, refWatcher);
}
return refWatcher;
}
上述代码中我们主要关注Install方法,该方法主要是开始引用的监听,在看其具体实现之前,我们应该有如下的疑问:
- 观察的引用对象具体指什么
- 怎么判断对象泄漏了
public static void install(Application application, RefWatcher refWatcher) {
//创建ActivityRefWatcher对象并调用其watchActivities方法
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
public void watchActivities() {
// 不要注册两次,因为registerActivityLifecycleCallbacks方法是直接向ArrayList中添加,并不去重
stopWatchingActivities();
//通过application注册一个activity的生命周期回调,但是也只能监听到activity的生命周期,说明这里观察的引用对象实际类型是activity
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
...省略空实现
@Override public void onActivityDestroyed(Activity activity) {
//当activity销毁时调用onActivityDestroyed
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
上述install方法主要是观察activity的生命周期回调,当activity的onDestory方法回调时,说明其应该是无用对象,可以回收,接下来看一下其怎么判断activity是否已经被回收:
//watch方法有两个,其具体实现的方法是当前方法,前文调用的refWatcher.watch方法实际调用的是如下方法,其中referenceName为空长度为0的字符串“”
public void watch(Object watchedReference, String referenceName) {
...省略空检查和无效检查
//记录开始时间
final long watchStartNanoTime = System.nanoTime();
//生成一个唯一的key
String key = UUID.randomUUID().toString();
//将key添加Set集合,如果该retainedKeys集合没有某个key了,则说明该key对应的reference对象被回收了
retainedKeys.add(key);
//创建了一个KeyedWeakReference对象,其是WeakReference类型的子类,
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);//这里的queue是一个ReferenceQueue类型的对象
//异步去确认引用是否被回收
ensureGoneAsync(watchStartNanoTime, reference);
}
watch方法中需要注意的有两点:
- WeakReference + ReferenceQueue是怎么去监听到对象被回收
- 对象泄漏或者被回收时,需要做什么,在什么时机上做
关于对象的引用,我们知道在java中有四种,其区别如下:
- 强引用:平常申明的引用,比如 String a; 内存gc时,如果被外部引用,则不会回收
- 软引用:SoftReference类型,如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存
- 弱引用:WeakReference类型,只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
- 虚引用:PhantomReference类型,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收
在LeakCanary中使用的弱引用的大致流程是,当WeakReference包含的对象未被回收时,ReferenceQueue队列中没有该对象,如果WeakReference包含的对象被回收后,则会将WeakReference添加到ReferenceQueue中,此时从ReferenceQueue队列中能获取到WeakReference对象。关于WeakReference + ReferenceQueue之间的关系,可以看一下这两篇文章:实践篇,原理篇
而ensureGoneAsync方法的流程如下:
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
ensureGoneAsync主要就是向AndroidWatchExecutor对象中添加了一个Retryable对象,我们先看AndroidWatchExecutor的怎么去执行的,之后在看这个ensureGone方法
//AndroidWatchExecutor对象
public void execute(Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
//如果是主线程,则等待主线程空闲
waitForIdle(retryable, 0);
} else {
//如果不是主线程,则切换到主线程,再调用waitForIdle
postWaitForIdle(retryable, 0);
}
}
//向主线程添加一个IdleHandler,该对象会在主线程空闲时被调用,详细的流程可以看下MessageQueue的next方法
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;//返回false表示该IdleHandler只会被执行一次
}
});
}
AndroidWatchExecutor的execute方法主要是切换到主线程,等待主线程空闲时会调用IdleHandle的queueIdle方法,再调用postToBackgroundWithDelay方法
void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
long delayMillis = initialDelayMillis * exponentialBackoffFactor;
//后台线程中执行Retryable对象的run方法,实际就是调用ensureGone方法
backgroundHandler.postDelayed(new Runnable() {
@Override public void run() {
Retryable.Result result = retryable.run();//执行ensureGone
//如果此时返回RETRY,表明需要再次尝试执行retryable.run();
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
下面就看下在后台线程中执行的ensureGone的实现:
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
//尝试移出retainedKeys中的key值
removeWeaklyReachableReferences();
//判断引用是被回收,gone方法就是判断retainedKeys的key值是否存在
if (gone(reference)) {
return DONE;//返回DONE表明不需要再次尝试执行retryable.run();
}
//尝试通知jvm去执行gc
gcTrigger.runGc();
//再次尝试移出retainedKeys中的key值
removeWeaklyReachableReferences();
//再次判断引用是被回收
if (!gone(reference)) {
//表示该引用没有被回收
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;//返回RETRY,表明需要再次尝试执行retryable.run();
}
....省略内存堆分析的代码
}
return DONE;
}
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
//如果ReferenceQueue队列中存在KeyedWeakReference对象,则会移出retainedKeys中的该对象的key,retainedKeys中的key实在RefWatch的watch方法中添加的
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}