美文网首页Android技术知识
LeakCanary 原理分析

LeakCanary 原理分析

作者: VictorLiang | 来源:发表于2017-08-23 18:23 被阅读233次

LeakCanary 是Android开发中常用的内存泄漏检测工具。
我们以Activity的监控为例,分析下是如何做泄漏分析的。

Activity监控

首先看LeakCanary初始化时做的操作:

  /**
   * Creates a {@link RefWatcher} that works out of the box, and starts watching activity
   * references (on ICS+).
   */
  public static RefWatcher install(Application application) {
    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
  }

buildAndInstall 方法里面启动了ActivityRefWatcher:

/**
   * Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+).
   */
  public RefWatcher buildAndInstall() {
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
      LeakCanary.enableDisplayLeakActivity(context);
      ActivityRefWatcher.install((Application) context, refWatcher);
    }
    return refWatcher;
  }

ActivityRefWatcher里面有对Activity生命周期的监控:

private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new Application.ActivityLifecycleCallbacks() {
        @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        }

        @Override public void onActivityStarted(Activity activity) {
        }

        @Override public void onActivityResumed(Activity activity) {
        }

        @Override public void onActivityPaused(Activity activity) {
        }

        @Override public void onActivityStopped(Activity activity) {
        }

        @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        @Override public void onActivityDestroyed(Activity activity) {
          ActivityRefWatcher.this.onActivityDestroyed(activity);
        }
      };
      
private final RefWatcher refWatcher;      
void onActivityDestroyed(Activity activity) {
    refWatcher.watch(activity);
  }

所以RefWatcher里面是对象引用监控的核心代码。下面我们来看下RefWarcher里面具体是如何做监控的。

RefWatcher 如何工作

RefWatcher属于LeakCanary 的leakcanary-watcher包下,是Java通用而非Android特有。

RefWarcher 提供watch方法,在对象被回收之后调用RefWatch的watch方法即可监控对象是否发生了泄漏。

private final ReferenceQueue<Object> queue;
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);
  }
补充知识: ReferenceQueue

Reference queues, to which registered reference objects are appended by the garbage collector after the appropriate reachability changes are detected.

可以简单的理解为,创建对象的WeakReference时,可以传入一个ReferenceQueue,当对象变为仅弱引用可达时,会将WeakReference的包装对象放入这个队列。可以通过检查ReferenceQueue队列中是否存在WeakReference这个包装对象来判断一个实例是否被回收。

LeakCanary 通过以下流程判断是否发生内存泄漏:

RefWatch.java 中 ensureGone 的流程

HAHA分析Dump的结果会通过回调的形式返回。可以得到泄漏的对象的引用路径。
PS.
HAHA(Headless Android Heap Analyzer)是Square出的Dump分析工具。

检测的时机

RefWatcher 的 watch 方法会异步做对象是否被回收的检测。

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

可以看到,其实调用是通过WatchExecutor watchExecutor来调用的。我们关注下WatchExecutor的Android实现类AndroidWatchExecutor:

@Override public void execute(Retryable retryable) {
    if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
      waitForIdle(retryable, 0);
    } else {
      postWaitForIdle(retryable, 0);
    }
  }
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;
      }
    });
  }

可以看到都是在主线程调用的 Looper.myQueue().addIdleHandler()方法。

补充知识2 MessageQueue.addIdleHandler
   /**
     * Callback interface for discovering when a thread is going to block
     * waiting for more messages.
     */
    public static interface IdleHandler {
        /**
         * Called when the message queue has run out of messages and will now
         * wait for more.  Return true to keep your idle handler active, false
         * to have it removed.  This may be called if there are still messages
         * pending in the queue, but they are all scheduled to be dispatched
         * after the current time.
         */
        boolean queueIdle();
    }

MessageQueue.addIdleHandler 可以简单理解为MQ提供的一个接口,允许MQ在队列空闲的时候做些事情。比如在空闲的时候做个GC啊。

补充知识3 如何手动GC

直接上代码:

  @Override public void runGc() {
      // Code taken from AOSP FinalizationTest:
      // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
      // java/lang/ref/FinalizationTester.java
      // System.gc() does not garbage collect every time. Runtime.gc() is
      // more likely to perfom a gc.
      Runtime.getRuntime().gc();
      enqueueReferences();
      System.runFinalization();
    }

白名单机制

RefWatcher 构造器支持设计白名单。

 /** @see ExcludedRefs */
  public final T excludedRefs(ExcludedRefs excludedRefs) {
    this.excludedRefs = excludedRefs;
    return self();
  }

目前Android系统级别的一些内存泄漏已经加入了默认的白名单中AndroidExcludedRefs.java。
用户也可以根据自己的需要自创建ExcludedRefs的实例添加到白名单Set中。

结果展示

Heap Dump 分析成功后会回调 onHeapAnalyzed 方法。

  /**
   * Called after a heap dump is analyzed, whether or not a leak was found.
   * Check {@link AnalysisResult#leakFound} and {@link AnalysisResult#excludedLeak} to see if there
   * was a leak and if it can be ignored.
   *
   * This will be called from a background intent service thread.
   * <p>
   * It's OK to block here and wait for the heap dump to be uploaded.
   * <p>
   * The heap dump file will be deleted immediately after this callback returns.
   */
  protected abstract void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result);

默认的实现类 是 DisplayLeakService。它会把Leak信息默认显示成通知格式。
当然也提供了供用户重写的方法,允许用户对检测结果做后续操作,保存\上报等。

  /**
   * You can override this method and do a blocking call to a server to upload the leak trace and
   * the heap dump. Don't forget to check {@link AnalysisResult#leakFound} and {@link
   * AnalysisResult#excludedLeak} first.
   */
  protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
  }

总结

LeakCanary核心思想是通过ReferenceQueue来检测对象是否被回收。在主线程空闲时才会做检测。 有必要的情况下会先手动出发GC。GC后还有引用会Dump Heap,通过HAHA做分析获取引用路径。

相关文章

网友评论

    本文标题:LeakCanary 原理分析

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