核心原理
个人觉得应该是这句话:
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.
如果一个对象变得弱可达,那么指向该对象的弱引用会立即被加到关联的引用队列。
LeakCanary的整体思路是:Activity/Fragment等对象在onDestroy回调方法里,构造一个弱引用对象,然后GC。若果activity/fragment对象没有泄漏的话,该引用会被加到关联的引用队列queue里,所以引用队列queue不包含该引用的话就有可能是内存泄漏。
初始化
Application里调用:
LeakCanary.install(this);
最新版的LeakCanary不需要显示地调用install初始化,而是在AndroidManifest里声明了一个ContentProvider:LeakSentryInstaller。ContentProvider的onCreate方法执行时机在Application的attachBaseContext() 和 onCreate() 方法之间。
install 方法:
public static RefWatcher install(Application application) {
return refWatcher(application)
.listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) //排除不需要监控的ref类型
.buildAndInstall();
}
buildAndInstall方法:
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) { //watchActivities默认为true
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) { //watchFragments 默认为true
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
ActivityRefWatcher.install:
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); //注册Activity生命周期回调监听
}
activityRefWatcher.lifecycleCallbacks:
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) { //在Activity的onDestroy方法里检测是否有泄漏
refWatcher.watch(activity);
}
};
fragment的检测类似
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
for (FragmentRefWatcher watcher : fragmentRefWatchers) {
watcher.watchFragments(activity); //也是在Activity的onDestroy回调里,启用FragmentRefWatcher去检测
}
}
};
FragmentRefWatcher.watch
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); //检测view有没有泄漏
}
}
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
refWatcher.watch(fragment); //检测fragment有没有泄漏
}
};
@Override public void watchFragments(Activity activity) {
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
RefWatcher.watch方法
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
/**
* 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); //把key加到retainedKeys
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue); //构建一个KeyedWeakReference对象,传入ReferenceQueue<Object> queue
ensureGoneAsync(watchStartNanoTime, reference);
}
KeyedWeakReference
final class KeyedWeakReference extends WeakReference<Object> {
public final String key;
public final String name;
KeyedWeakReference(Object referent, String key, String name, ReferenceQueue<Object> referenceQueue) {
//将需要监测的对象和关联队列作为构造方法参数,构造新的弱引用
super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
this.key = checkNotNull(key, "key");
this.name = checkNotNull(name, "name");
}
}
关键方法ensureGoneAsync:
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() { //watchExecutor是AndroidWatchExecutor实例,内部有个HandlerThread
@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(); //把已被回收的对象的 key 从 retainedKeys 移除
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) { //如果retainedKeys不包含该对象的key,则表示该对象已经被回收,返回DONE
return DONE;
}
gcTrigger.runGc(); //手动GC,确保弱可达的对象的引用入队
removeWeaklyReachableReferences(); //再次把已被回收的对象的 key 从 retainedKeys 移除
if (!gone(reference)) { //如果retainedKeys众依然存在该对象的key,说明泄漏了
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);
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
}
return DONE;
}
removeWeaklyReachableReferences:
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.
// 如果一个对象变得弱可达,该对象的弱引用会立即被加到关联的queue队列。这个步骤在真正的垃圾回收执行之前
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) { //queue的引用是将会被回收的对象引用
retainedKeys.remove(ref.key); //将这些引用从retainedKeys里移除
}
}
gone:
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key); //判断retainedKeys是否包含检测对象的引用
}
AndroidWatchExecutor
@Override public void execute(@NonNull Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
postWaitForIdle(retryable, 0); //如果当前线程是子线程,还是会通过mainHandler切换到主线程
}
}
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
mainHandler.post(new Runnable() {
@Override public void run() {
waitForIdle(retryable, failedAttempts);
}
});
}
private void waitForIdle(final Retryable retryable, final int failedAttempts) { //最终都是调waitForIdle方法
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { //添加到空闲mIdleHandlers列表
@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;
backgroundHandler.postDelayed(new Runnable() { //在子线程分析
@Override public void run() {
Retryable.Result result = retryable.run();
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
为什么不是虚引用
面试被问到了。
虚引用必须和引用队列 ReferenceQueue 联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
虚引用在回收前就会被加到引用队列里,就算是内存泄漏了,也会在引用队列里,无法检测是否泄漏。
网友评论