美文网首页Android 原理
LeakCanary内存泄漏监听原理

LeakCanary内存泄漏监听原理

作者: 魔焰之 | 来源:发表于2019-04-10 19:43 被阅读0次

    大致流程:

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

    相关文章

      网友评论

        本文标题:LeakCanary内存泄漏监听原理

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