美文网首页
LeakCanary 工作原理浅析

LeakCanary 工作原理浅析

作者: 大盗海洲 | 来源:发表于2019-06-16 11:18 被阅读0次

    参考链接
    Java内存问题 及 LeakCanary 原理分析
    LeakCanary 工作原理浅析

    通过 application.registerActivityLifecycleCallbacks 监听activity onActivityDestroy()
    用过WeakReference+ReferenceQueue 存储当前activity 的引用,手动调用gc
    然后判断ReferenceQueue队列是否含有此activity的引用,若不包含,说明被强引用,存在内存泄漏,然后导出heap dump 文件,用haha算法分析,在发送到通知栏里

    public final class ActivityRefWatcher {
     public static void install(Context context, RefWatcher refWatcher) {
        Application application = (Application) context.getApplicationContext();
        ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
        application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
      }
    
      private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
          new ActivityLifecycleCallbacksAdapter() {
            @Override public void onActivityDestroyed(Activity activity) {
              refWatcher.watch(activity);
            }
          };
    }
    
    public final class RefWatcher {
     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);
    //创建WeakReference
        final KeyedWeakReference reference =
            new KeyedWeakReference(watchedReference, key, referenceName, queue);
    
        ensureGoneAsync(watchStartNanoTime, reference);
      }
    }
    
    public final class RefWatcher {
    
      @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();//先将引用尝试从队列中poll出来
    
        if (debuggerControl.isDebuggerAttached()) {//规避调试模式
          // The debugger can create false leaks.
          return RETRY;
        }
        if (gone(reference)) {//检测是否已经回收
          return DONE;
        }
            //如果没有被回收,则手动GC
        gcTrigger.runGc();
        removeWeaklyReachableReferences();//再次尝试poll,检测是否被回收
        if (!gone(reference)) {
    // 还没有被回收,则dump堆信息,调起分析进程进行分析
          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;
      }
    
      private boolean gone(KeyedWeakReference reference) {
        return !retainedKeys.contains(reference.key);
      }
    
      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.
        KeyedWeakReference ref;
        while ((ref = (KeyedWeakReference) queue.poll()) != null) {
          retainedKeys.remove(ref.key);
        }
      }
    }
    

    方法ensureGone中通过检测referenceQueue队列中的引用情况,来判断回收情况,通过手动GC来进一步确认回收情况。
    整个过程肯定是个耗时卡UI的,整个过程会在WatchExecutor中执行的,那WatchExecutor又是在哪里执行的呢?
    LeakCanary已经利用Looper机制做了一定优化,利用主线程空闲的时候执行检测任务,这里找到WatchExecutor的实现类,研究下原理:

    public final class AndroidWatchExecutor implements WatchExecutor {
    
      private 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;
          }
        });
      }
    }
    

    知识点

    • 用ActivityLifecycleCallback 监听activity生命周期
    • 用WeekReference+ReferenceQueue 来监听activity回收情况
    • Application 中可通过processName 判断是否任务执行进程
    • MessageQueue 中加入一个IdleHandler 来得到主线程空闲的回调
    • 只能监听activity里相关类内存泄漏,其他类无法使用,还得用MAT原始方法

    相关文章

      网友评论

          本文标题:LeakCanary 工作原理浅析

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