美文网首页
LeakCanary

LeakCanary

作者: 拿拿guardian | 来源:发表于2020-04-26 14:43 被阅读0次

    核心原理

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

    相关文章

      网友评论

          本文标题:LeakCanary

          本文链接:https://www.haomeiwen.com/subject/qelayhtx.html