LeakCanary内存泄漏监听原理

大致流程:

  // 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);
    }
  }

你可能感兴趣的:(android,LeakCanary,内存泄漏,主线程空闲,reference,referencequeue)