Android内存泄漏框架LeakCanary源码分析

LeakCanary源码分析

LeakCanary是一个内存泄漏检测的框架,默认只会检测Activity的泄漏,如果需要检测其他类,可以使用LeakCanary.install返回的RefWatcher,调用RefWatcher.watch(obj)就可以观测obj对象是否出现泄漏。

从install方法开始:

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

最终返回了一个RefWatcher对象:

  public RefWatcher buildAndInstall() {
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
      LeakCanary.enableDisplayLeakActivity(context);
      ActivityRefWatcher.install((Application) context, refWatcher);
    }
    return refWatcher;
  }

DisplayLeakService

继承自AbstractAnalysisResultService,它是一个IntentService,用来对泄漏数据进行分析处理。

 public abstract class AbstractAnalysisResultService extends IntentService {

     ...

    @Override protected final void onHandleIntent(Intent intent) {
        HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
        AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
        try {
          onHeapAnalyzed(heapDump, result);
        } finally {
          //noinspection ResultOfMethodCallIgnored
          heapDump.heapDumpFile.delete();
        }
   }
 }

在AbstractAnalysisResultService的实现DisplayLeakService中,会开启一个DisplayLeakActivity活动进行泄漏的显示:

 public class DisplayLeakService extends AbstractAnalysisResultService {

  @Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
    String leakInfo = leakInfo(this, heapDump, result, true);
    CanaryLog.d("%s", leakInfo);

    boolean resultSaved = false;
    boolean shouldSaveResult = result.leakFound || result.failure != null;
    if (shouldSaveResult) {
      heapDump = renameHeapdump(heapDump);
      resultSaved = saveResult(heapDump, result); //保存heapdump到本地
    }

    PendingIntent pendingIntent;
    String contentTitle;
    String contentText;

    if (!shouldSaveResult) {
      contentTitle = getString(R.string.leak_canary_no_leak_title);
      contentText = getString(R.string.leak_canary_no_leak_text);
      pendingIntent = null;
    } else if (resultSaved) { //heapdump文件保存成功才开启活动
      pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);

      if (result.failure == null) {
        String size = formatShortFileSize(this, result.retainedHeapSize);
        String className = classSimpleName(result.className);
        if (result.excludedLeak) {
          contentTitle = getString(R.string.leak_canary_leak_excluded, className, size);
        } else {
          contentTitle = getString(R.string.leak_canary_class_has_leaked, className, size);
        }
      } else {
        contentTitle = getString(R.string.leak_canary_analysis_failed);
      }
      contentText = getString(R.string.leak_canary_notification_message);
    } else {
      contentTitle = getString(R.string.leak_canary_could_not_save_title);
      contentText = getString(R.string.leak_canary_could_not_save_text);
      pendingIntent = null;
    }
    // New notification id every second.
    int notificationId = (int) (SystemClock.uptimeMillis() / 1000);
    showNotification(this, contentTitle, contentText, pendingIntent, notificationId);
    afterDefaultHandling(heapDump, result, leakInfo);   //最后调用一个扩展的接口,我们可以重写这个方法来实现heapdump上传到服务器的功能
  }
 }

RefWatch

RefWatch是最主要的类:
我们看一下watch方法:

   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);  //retainedKeys存放未被GC回收的被观察对象
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue); //给被观测的对象创建一个弱引用

    ensureGoneAsync(watchStartNanoTime, reference); //开始异步对目标进行观察
  }

加入一个任何到执行器中:

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

检查引用是否有效;GC;再次检查引用是否有效。
弱引用的关联引用队列:当弱引用的对象,弱引用会放入关联的引用队列queue中。

  Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();   //gc开始时间
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);  //已经观察的时间

    removeWeaklyReachableReferences();  //把有效引用(线程可达)移出弱引用集合

    if (debuggerControl.isDebuggerAttached()) {
      // The debugger can create false leaks.
      return RETRY;
    }
    if (gone(reference)) {  //如果弱引用队列里面已经没有被观测对象的引用了,表示这个对象已经被回收。
      return DONE;
    }
    gcTrigger.runGc();  //执行GC,并且等待100ms
    removeWeaklyReachableReferences();  //再次移出有效引用
    if (!gone(reference)) { //如果这时候被观测对象的引用不等于null,也就是它仍然被线程可达,说明已经发生了内存泄漏
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); //计算gc所用时间

      File heapDumpFile = heapDumper.dumpHeap();    //把堆栈信息存到本地
      if (heapDumpFile == RETRY_LATER) {
        // Could not dump the heap.
        return RETRY;
      }
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
      heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
              gcDurationMs, heapDumpDurationMs));   //最后交给heapdump监听器来处理
    }
    return DONE;
  }

heapdump信息最终会传递给继承自IntentService的listenerServiceClass方法处理:

   public static void runAnalysis(Context context, HeapDump heapDump,
      Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
    Intent intent = new Intent(context, HeapAnalyzerService.class);
    intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
    intent.putExtra(HEAPDUMP_EXTRA, heapDump);
    context.startService(intent);
  }

AndroidWatchExecutor

我们来看一下内存泄漏检查的执行器:

   public AndroidWatchExecutor(long initialDelayMillis) {
    mainHandler = new Handler(Looper.getMainLooper());  //主线程Handler
    HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);   //新建一个带消息循环Looper的子线程
    handlerThread.start();
    backgroundHandler = new Handler(handlerThread.getLooper()); //后台Handler
    this.initialDelayMillis = initialDelayMillis;
    maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
  }

执行检查任务:

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

最终是在主线程调用waitForIdle:

  void waitForIdle(final Retryable retryable, final int failedAttempts) {
    // This needs to be called from the main thread.
    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {    //添加一个闲置处理器(在主线程消息队列闲置的时候会执行queueIdle)
      @Override public boolean queueIdle() {
        postToBackgroundWithDelay(retryable, failedAttempts);
        return false;
      }
    });
  }

    void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
    long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
    long delayMillis = initialDelayMillis * exponentialBackoffFactor;
    backgroundHandler.postDelayed(new Runnable() {  //把任务放到后台线程的消息队列中
      @Override public void run() {
        Retryable.Result result = retryable.run();  //这时候才会执行针对对象的内存泄漏检查
        if (result == RETRY) {
          postWaitForIdle(retryable, failedAttempts + 1);
        }
      }
    }, delayMillis);
  }

ActivityRefWatcher

这个类是用来监听一个应用内的所有活动的生命周期,当活动执行了onDestory的时候,开始对Activity内存泄漏的检查。

   void onActivityDestroyed(Activity activity) {
    refWatcher.watch(activity);
  }

总结

整个Activity内存泄漏的检测从Activity执行了onDestory后开始:
1. Activity执行onDestory
2. refWatcher把该Activity添加到弱引用列表里面
3. 然后使用IdelHandler机制,在主线程空闲的时候延时 5 秒发送一个Runnable到后台线程HandlerThread的消息队列里
4. HandlerThread取出消息,移除被回收对象的弱引用,然后查找活动是否还存在于弱引用列表中,如果不在,则说明没有发生内存泄漏
5. 如果在,则调用一次gc,等待100ms,再次移除被回收对象的弱引用,查找活动是否还存在弱引用列表里面,如果在,则说明发生了内存泄漏
6. 然后就执行dumpheap,把堆栈信息用Intent的方式发送给IntentService
7. IntentService拿到dumheap之后,保存heapdump到本地,在通知栏上显示内存泄漏的通知,然后执行afterDefaultHandling(我们可以重写)
8. 最后删除本地的heapdump文件

几个问题
1. 为什么要使用idlehandler? 在主线程空闲的时候才把任务放到子线程中执行
2. HandlerThread会退出吗?
改进,用HandlerThread设置为守护线程,当主线程退出时,该线程也就死亡了:
handlerThread.setDaemon(true);
3. 线程安全吗?安全retainedKeys = new CopyOnWriteArraySet<>();

猜你喜欢

转载自blog.csdn.net/mingC0758/article/details/81350974