LeakCanary原理解析,理解起来超简单!

一.引言

LeakCanary我相信大家都不会陌生的,在我们的开发过程中,为了避免内存泄漏的问题,我们可以在我们的项目中集成LeakCanary,来观察我们的应用程序是存在内存泄漏的问题。今天我们就来看看LeakCanary的原理,了解它是怎么去判断我们的应用程序是发生了内存泄漏的。

二.源码解析

在LeakCanary的源码中使用到了一个非常关键的数据结构,这个数据结构是LeakCanary判断应用程序是否发生了内存泄漏泄漏的关键。这个数据结构就是ReferenceQueue。

1.ReferenceQueue
从名字就可以听出来,ReferenceQueue是一种存放引用的队列。我们知道在Java中有四种引用。
1.强引用(当我们创建一个对象时,默认创建的就是强引用。只要强引用还存在,垃圾回收器就算抛出OOM,也不会回收强引用引用的对象。)
2.软引用(SoftReference,当内存不足时,垃圾回收器会回收被引用的对象。)
3.弱引用(WeakReference,当GC时垃圾回收器会回收掉被引用的对象。)
4.虚引用 (PhantomReference,基本不会用到。)
我们的ReferenceQueue既然是一个存放引用的队列,那它原理是什么呢?我们先来看下WeakReference的源码:

public class WeakReference<T> extends Reference<T> {
    public WeakReference(T referent) {
        super(referent);
    }

    
    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

}

可以看到WeakReference中有两个构造方法,第二个构造方法要求我们传入一个ReferenceQueue类型的对象。这个ReferenceQueue作用是什么呢?其实呢,这个ReferenceQueue对象,会在垃圾收集器即将回收引用对象指向的对象时,将这个引用对象加入这个队列。注意了,引用指向的对象是说的我们在构造WeakReference时构造方法中传的对象,引用对象说的就是我们这个引用本身,两者的概念不要弄混淆了。
举个例子:

 ReferenceQueue<Activity> mQueue = new ReferenceQueue<>();
 WeakReference<Activity> mWeakReference = new WeakReference<Activity>(mActivity,mQueue);

以上面代码举例,如果GC时将mWeakReference指向的mActivity回收的话,同时也会向我们的mQueue中加入我们的mWeakReference。关于ReferenceQueue暂时我们就了解这么多就可以了,有兴趣的朋友可以自己去了解下。

2.LeakCanary.install(this);
关于LeakCanary的使用,一般我们都是在Application的onCreate的方法中,调用LeakCanary.install(this),下面我们就来分析分析通过这一行代码,LeakCanary究竟为我们做了哪些事。

  public static RefWatcher install(Application application) {
    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
  }

(1)调用refWatcher()创建了一个AndroidRefWatcherBuilder。


  public static AndroidRefWatcherBuilder refWatcher(Context context) {
    return new AndroidRefWatcherBuilder(context);
  }

(2)调用AndroidRefWathcerBuilder的listenerServiceClass方法注册一个回调。

 public AndroidRefWatcherBuilder listenerServiceClass(
      Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
      //包装了两层
    return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
  }

(3)调用AndroidRefWatcherBuilder的excludedRefs方法


  @Override protected ExcludedRefs defaultExcludedRefs() {
    return AndroidExcludedRefs.createAppDefaults().build();
  }

这里主要是为了排除系统导致的内存泄漏。
(4)buildAndInstall()

public RefWatcher buildAndInstall() {
    //做了判断install方法只能被调用一次
    if (LeakCanaryInternals.installedRefWatcher != null) {
      throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
    }
    //创建一个RefWacher观察对象
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
      if (watchActivities) {
        ActivityRefWatcher.install(context, refWatcher);
      }
      if (watchFragments) {
        FragmentRefWatcher.Helper.install(context, refWatcher);
      }
    }
    LeakCanaryInternals.installedRefWatcher = refWatcher;
    return refWatcher;
  }

主要作用创建了一个RefWatcher对象并返回,这个方法里面有两个比较重要的方法我们看下
ActivityRefWatcher.install(context, refWatcher)和FragmentRefWatcher.Helper.install(context, refWatcher);

(5) ActivityRefWatcher.install(context, refWatcher);

  public static void install(Context context, RefWatcher refWatcher) {
    Application application = (Application) context.getApplicationContext();
    ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
    //为当前Application注册了所有Activity的生命周期的回调
    application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
  }

  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new ActivityLifecycleCallbacksAdapter() {
        @Override public void onActivityDestroyed(Activity activity) {
         //当Activity销毁时调用refWathcer的watch方法观察这个对象
          refWatcher.watch(activity);
        }
      };

(6) FragmentRefWatcher.Helper.install(context, refWatcher);

 public static void install(Context context, RefWatcher refWatcher) {
      //创建一个集合
      List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();

     //如果当前API版本大于26,创建一个AndroidOFragmentRefWatcher对象加入到集合中
      if (SDK_INT >= O) {
        fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
      }

      try {
        //找到SupportFragmentRefWatcher这个类
        Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
        //找到它的构造方法
        Constructor<?> constructor =
            fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
            //创建SupportFragmentRefWatcher实例,并添加到集合中
        FragmentRefWatcher supportFragmentRefWatcher =
            (FragmentRefWatcher) constructor.newInstance(refWatcher);
        fragmentRefWatchers.add(supportFragmentRefWatcher);
      } catch (Exception ignored) {
      }

      if (fragmentRefWatchers.size() == 0) {
        return;
      }

      Helper helper = new Helper(fragmentRefWatchers);

      Application application = (Application) context.getApplicationContext();
      application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
    }

这个方法的作用是当当前API版本大于26的时候,在Activity销毁是它会调用AndroidOFragmentRefWatcher的watchFragments方法观察当前Activity中的所有销毁的Fragments。也就是说如果当前API版本是大于26的,我们不需要在在Fragment的onDestory方法中自己调用RefWatcher的watch方法了。

下面我们来分析RefWatcher的watch方法,这个方法是LeakCanary的核心逻辑所在,我们一起来看看吧。

3.RefWatcher.watch();

  public void watch(Object watchedReference) {
    watch(watchedReference, "");
  }

  public void watch(Object watchedReference, String referenceName) {
    if (this == DISABLED) {
      return;
    }
    checkNotNull(watchedReference, "watchedReference");
    checkNotNull(referenceName, "referenceName");
    //获取时间,纳秒级别
    final long watchStartNanoTime = System.nanoTime();
    //生成一个随机数作为key
    String key = UUID.randomUUID().toString();
    
    //retainedKeys是一个Set集合,存储所有的key
    retainedKeys.add(key);
    
    //使用KeyedWeakReference包装当前传过来的引用,代指Activity和Fragment
    //注意了这里传了一个queue,即ReferenceQueue,所以如果KeyedWeakReference引用的Activity或者
    //Fragment即将被销毁时,就会向这个queue中添加reference这个引用对象。
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);

    ensureGoneAsync(watchStartNanoTime, reference);
  }

4.ensureGoneAsync()

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

这里主要就是调用watchExecutor的execute添加了一个Retryable任务,有点类似与线程池。这边的watchExecutor是通过RefWathcer的构造方法传过来的。通过上面的分析,我们知道在一开始创建了一个AndroidRefWatcherBuilder对象,它会调用的buildAndInstall方法,这个方法中我们调用了build()方法创建了RefWatcher对象,现在我们去看看这个RefWathcer是怎么创建的。

5.build()

 public final RefWatcher build() {
    if (isDisabled()) {
      return RefWatcher.DISABLED;
    }

    if (heapDumpBuilder.excludedRefs == null) {
      heapDumpBuilder.excludedRefs(defaultExcludedRefs());
    }

    HeapDump.Listener heapDumpListener = this.heapDumpListener;
    if (heapDumpListener == null) {
      heapDumpListener = defaultHeapDumpListener();
    }

    DebuggerControl debuggerControl = this.debuggerControl;
    if (debuggerControl == null) {
      debuggerControl = defaultDebuggerControl();
    }

    HeapDumper heapDumper = this.heapDumper;
    if (heapDumper == null) {
     //调用的是AndroidRefWatcherBuilder中的方法。所以这里创建的是一个AndroidHeapDumper对象
      heapDumper = defaultHeapDumper();
    }

    WatchExecutor watchExecutor = this.watchExecutor;
    if (watchExecutor == null) {
      //调用的是AndroidRefWatcherBuilder中的方法。所以这里创建的是一个AndroidWatchExecutor对象
      watchExecutor = defaultWatchExecutor();
    }

    GcTrigger gcTrigger = this.gcTrigger;
    if (gcTrigger == null) {
      //创建的是GcTrigger DEFAULT这个对象
      gcTrigger = defaultGcTrigger();
    }

    if (heapDumpBuilder.reachabilityInspectorClasses == null) {
      heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
    }

    return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
        heapDumpBuilder);
  }

这里注意了,AndroidRefWatcherBuilder类中没用实现build方法,所有是调用父类RefWatcherBuilder中的build方法,但是AndroidRefWatcherBuilder实现了defaultWatchExecutor,defaultHeapDumpListener这些方法,所以这些方法应该调用的是AndroidRefWatcherBuilder中的,而不是RefWatcherBuilder的。

6.AndroidWatchExecutor.execute()
通过5的分析我们知道RefWatcher中的watchExecutor对象其实是AndroidWatchExecutor类型的,下面我们来看看它的execute方法做了什么事。

  @Override public void execute(Retryable retryable) {
    //如果当前线程是主线程
    if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
      waitForIdle(retryable, 0);
    } else {
      postWaitForIdle(retryable, 0);
    }
  }

我们看到,这里分了两种情况去做处理。如果当前线程是主线程调用 waitForIdle(retryable, 0);否则调用postWaitForIdle(retryable, 0);。其实不管怎么样最终调用的都是waitForIdle这个方法。

1.waitForIdle(retryable, 0);

private void waitForIdle(final Retryable retryable, final int failedAttempts) {
    //向主线程的消息队列中里添加了一个对象
    //当我们注册了IdleHandler的时候,当主线程空闲时,会发送一个空闲消息来执行IdleHandler中的回调
    //注意了,queueIdle这个返回为false是,表示只会执行一次,如果为true代表,如果主线程每次空闲时都会
    //执行这个方法
    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
      @Override public boolean queueIdle() {
        //当主线程空闲时调用postToBackgroundWithDelay方法
        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是一个与异步线程绑定的Handler,我们可以看下AndroidWatchExecutor的构造方法中
    //创建了一个HandlerThread,自己内部构建了一个异步的消息循环
    //所以retryable.run()方法是执行在异步线程中的
    backgroundHandler.postDelayed(new Runnable() {
      @Override public void run() {
        Retryable.Result result = retryable.run();
        if (result == RETRY) {
          postWaitForIdle(retryable, failedAttempts + 1);
        }
      }
    }, delayMillis);
  }

7.retryable.run() ->ensureGone
上面分析了那么多,其实这里才是我们的主要逻辑,我们先把涉及到的关键代码,贴出来,一步步来分析。

 @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);
    
    //如果对象GC时被回收了,就移除retainedKeys中对应的key值
    removeWeaklyReachableReferences();

    if (debuggerControl.isDebuggerAttached()) {
      // The debugger can create false leaks.
      return RETRY;
    }
    
    //如果retainedKeys还存在观察对象的key值代表没有被回收
    if (gone(reference)) {
      return DONE;
    }
    
    //第一次检查没有被回收手动触发GC
    gcTrigger.runGc();

   //GC完毕之后再去检查
    removeWeaklyReachableReferences();
    
   //如果GC后retainedKeys还存在观察对象的key。则表示发生了内存泄漏。
    if (!gone(reference)) {
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

     //dump生成文件
      File heapDumpFile = heapDumper.dumpHeap();
      if (heapDumpFile == RETRY_LATER) {
        // Could not dump the heap.
        return RETRY;
      }
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);

      //创建了HeapDump对象
      HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
          .referenceName(reference.name)
          .watchDurationMs(watchDurationMs)
          .gcDurationMs(gcDurationMs)
          .heapDumpDurationMs(heapDumpDurationMs)
          .build();
          
     //回调ServiceHeapDumpListener的analyze方法,然后调用HeapAnalyzerService.runAnalysis的静态方法
     //启动自身去分析HeapDump,使用HaHa库。不在做分析,感兴趣的朋友可以自己去研究研究。
      heapdumpListener.analyze(heapDump);
    }
    return DONE;
  }


  private boolean gone(KeyedWeakReference reference) {
    //判断是否还存在当前引用的key
    //如果存在代表没有被回收
    //不存在代表被回收了
    return !retainedKeys.contains(reference.key);
  }

  private void removeWeaklyReachableReferences() {
    //如果队列中有元素,代表GC是已经将对象给回收掉
    //每从队列中去除一个元素,同时在retainedKeys这个集合中移除相应的key值。
    KeyedWeakReference ref;
    while ((ref = (KeyedWeakReference) queue.poll()) != null) {
      retainedKeys.remove(ref.key);
    }
  }


  GcTrigger DEFAULT = new GcTrigger() {
    @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.
      //这里为什没有调用System.gc()呢,注释已经给了我们说明
      //Runtime.gc()比起System.gc()更有可能去触发一次GC,注意并不是百分百触发GC
      Runtime.getRuntime().gc();
      enqueueReferences();
      //建议在GC时执行对象的finalize方法,不是百分百
      System.runFinalization();
    }

    private void enqueueReferences() {
      // Hack. We don't have a programmatic way to wait for the reference queue daemon to move
      // references to the appropriate queues.
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        throw new AssertionError();
      }
    }
  };


到这里我们的LeakCanary如何去判断Activity和Fragment是否发生内存泄漏的主要逻辑就分析完了。我们来做一个总结。

三.总结

LeakCanary实现内存泄漏的主要判断逻辑是这样的。当我们观察的Activity或者Fragment销毁时,我们会使用一个弱引用去包装当前销毁的Activity或者Fragment,并且将它与本地的一个ReferenceQueue队列关联。我们知道如果GC触发了,系统会将当前的引用对象存入队列中。
如果没有被回收,队列中则没有当前的引用对象。所以LeakCanary会去判断,ReferenceQueue是否有当前观察的Activity或者Fragment的引用对象,第一次判断如果不存在,就去手动触发一次GC,然后做第二次判断,如果还是不存在,则表明出现了内存泄漏。

  • 11
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值