LeakCanary

2020-04-26  本文已影响0人  拿拿guardian

核心原理

个人觉得应该是这句话:
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 联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
虚引用在回收前就会被加到引用队列里,就算是内存泄漏了,也会在引用队列里,无法检测是否泄漏。

上一篇下一篇

猜你喜欢

热点阅读