2020-12-09

LeakCanary工作原理和源码解析

内存泄漏一直以来就是Android APP需要着重解决的点,而LeakCanary是一个开源的内存泄漏检测库,通过简单的配置就可以帮我们快速的获取和定位到内存泄漏的位置,这极大的提高了开发的效率和APP的性能。为了能更加好的理解Android中的内存管理机制,我们还是有必要了解下LeakCanary的工作原理的,本篇文章就来对LeakCanary的源码进行解析。

一、简单配置

在build.gradle中添加配置:
支持库的fragments
implementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
public class LeakApplication extends Application {

    private RefWatcher refWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = setupLeakCanary();
        //        refWatcher.watch(this);
    }

    private RefWatcher setupLeakCanary() {
        // 判断当前进程是否是LeakCanary用于分析heap内存的而创建的HeapAnalyzerService所在进程,
        // 如果是的话,则不进行Application中的初始化功能。
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return RefWatcher.DISABLED;
        }
        // 进行LeakCanary的安装配置
        return LeakCanary.install(this);
    }

    public static RefWatcher getRefWatcher(Context context) {
        LeakApplication leakApplication = (LeakApplication) context.getApplicationContext();
        return leakApplication.refWatcher;
    }
}

二、源码解析

1、先从LeakCanary#install开始看起:

/**
 * Creates a {@link RefWatcher} that works out of the box, and starts watching activity
 * references (on ICS+).
 */
public static @NonNull RefWatcher install(@NonNull Application application) {
  return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
      .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
      .buildAndInstall();
}

调用install方法,里面是链式调用,我们一个一个来分析

2、LeakCanary#refWatcher

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

AndroidRefWatcherBuilder(@NonNull Context context) {
    // 这里只是保存了ApplicationContext的引用
  this.context = context.getApplicationContext();
}

refWatcher方法就是简单创建了一个AndroidRefWatcherBuilder对象。

3、AndroidRefWatcherBuilder#listenerServiceClass

public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
    @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
  return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}

/** @see HeapDump.Listener */
public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
    this.heapDumpListener = heapDumpListener;
    return self();
}

public ServiceHeapDumpListener(@NonNull final Context context,
      @NonNull final Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
    this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
    this.context = checkNotNull(context, "context").getApplicationContext();
}

创建了ServiceHeapDumpListener对象并把DisplayLeakService传递给它的一个成员变量,然后把创建的ServiceHeapDumpListener赋值给AndroidRefWatcherBuilder的父类RefWatcherBuilder的成员变量listenerServiceClass,ServiceHeapDumpListener作用是接收一个head dump文件并启动服务(位于独立进程)对文件使用haha库进行解析,接下来可以看得到,这里先说下它的作用。

4、RefWatcherBuilder#excludedRefs

/** @see ExcludedRefs */
public final T excludedRefs(ExcludedRefs excludedRefs) {
  heapDumpBuilder.excludedRefs(excludedRefs);
  return self();
}

// com.squareup.leakcanary.HeapDump.Builder#excludedRefs
public Builder excludedRefs(ExcludedRefs excludedRefs) {
  this.excludedRefs = checkNotNull(excludedRefs, "excludedRefs");
  return this;
}

这个方法是用来记录Android SDK和一些手机厂商定制SDK的时候存在的内存泄漏情况,这些都会被LeakCanary的检测给过滤掉。

5、AndroidRefWatcherBuilder#buildAndInstall

public @NonNull RefWatcher buildAndInstall() {
  if (LeakCanaryInternals.installedRefWatcher != null) {
    throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
  }
  // 1
  RefWatcher refWatcher = build();
  if (refWatcher != DISABLED) {
    LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
    if (watchActivities) {
        // 2
      ActivityRefWatcher.install(context, refWatcher);
    }
    if (watchFragments) {
        // 3
      FragmentRefWatcher.Helper.install(context, refWatcher);
    }
  }
  LeakCanaryInternals.installedRefWatcher = refWatcher;
  return refWatcher;
}

1、RefWatcherBuilder#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) {
    heapDumper = defaultHeapDumper();
  }

  WatchExecutor watchExecutor = this.watchExecutor;
  if (watchExecutor == null) {
    watchExecutor = defaultWatchExecutor();
  }

  GcTrigger gcTrigger = this.gcTrigger;
  if (gcTrigger == null) {
    gcTrigger = defaultGcTrigger();
  }

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

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

可以看到,build方法是通过建造者模式构建RefWatcher,在RefWatcher的构造器中传递了多个参数:

  • watchExecutor:实现类是AndroidWatchExecutor,线程控制器,在onDestroy之后且主线程是空闲状态时执行内存泄漏检测。
  • debuggerControl:判断是否处于调试模式,调试模式中不会进行内存泄漏检测。
  • gcTrigger:用来触发gc操作。
  • heapDumper:实现类是AndroidHeapDumper,用来生成hprof文件。
  • heapDumpListener:这个字段就是上面说到的ServiceHeapDumpListener,封装了DisplayLeakService,主要用来开启一个HeapAnalyzerService服务对hprof文件使用haha库进行解析,最后通知会DisplayLeakService弹出泄漏提醒弹窗。
  • heapDumpBuilder.excludedRefs:就是我们上面说到的LeakCanary检测时可以忽略的泄漏路径。
  • heapDumpBuilder.reachabilityInspectorClasses:用于进行可达性检测的类列表。

2、leakcanary.ActivityRefWatcher#install

public static void install(@NonNull Context context, @NonNull 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);
        }
      };

这里主要实现了Application中的ActivityLifecycleCallbacks对Activity的生命周期进行监听,这样在Activity执行onDestroy之后调用refWatcher.watch(Activity)对Activity进行内存泄漏检测。

3、FragmentRefWatcher.Helper#install

public static void install(Context context, RefWatcher refWatcher) {
  List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();

  if (SDK_INT >= O) {
    // 1
    fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
  }

  try {
    Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
    Constructor<?> constructor =
        fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
    FragmentRefWatcher supportFragmentRefWatcher =
        (FragmentRefWatcher) constructor.newInstance(refWatcher);
    // 2
    fragmentRefWatchers.add(supportFragmentRefWatcher);
  } catch (Exception ignored) {
  }

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

    // 3
  Helper helper = new Helper(fragmentRefWatchers);

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

这个方法主要用来监视fragment的泄漏,在注释1中把标准的Fragment的RefWatcher类AndroidOFragmentRefWatcher添加到fragmentRefWatchers中,注释2是通过反射获取SupportFragmentRefWatcher添加到fragmentRefWatchers中,在注释3中创建helper,在注释4中调用了helper.activityLifecycleCallbacks。

private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
    new ActivityLifecycleCallbacksAdapter() {
      @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        for (FragmentRefWatcher watcher : fragmentRefWatchers) {
          watcher.watchFragments(activity);
        }
      }
    };

这里调用了watcher.watchFragments,这个watcher是AndroidOFragmentRefWatcher或者SupportFragmentRefWatcher,我们来看看AndroidOFragmentRefWatcher中watchFragments方法。

@Override public void watchFragments(Activity activity) {
  FragmentManager fragmentManager = activity.getFragmentManager();
  fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}

private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
      new FragmentManager.FragmentLifecycleCallbacks() {

        @Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
          View view = fragment.getView();
          if (view != null) {
            refWatcher.watch(view);
          }
        }

        @Override
        public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
          refWatcher.watch(fragment);
        }
      };

通过当前传入的activity获取到FragmentManager,并调用registerFragmentLifecycleCallbacks进行监听检测,也就是在onFragmentViewDestroyed和onFragmentDestroyed分别使用refWatcher.watch(view)和refWatcher.watch(fragment)进行内存泄漏的检测。


6、RefWatcher#watch(java.lang.Object)

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();
  // 使用随机的UUID保证了每个检测对象对应的key的唯一性
  String key = UUID.randomUUID().toString();
  // 将key加入到set集合retainedKeys中,在检测对象是否泄漏的情况下会用的上
  retainedKeys.add(key);
  // 1
  final KeyedWeakReference reference = 
                    new KeyedWeakReference(watchedReference, key, referenceName, queue);
  // 2
  ensureGoneAsync(watchStartNanoTime, reference);
}

1、com.squareup.leakcanary.KeyedWeakReference

final class KeyedWeakReference extends WeakReference<Object> {
  public final String key;
  public final String name;

  KeyedWeakReference(Object referent, String key, String name,
      ReferenceQueue<Object> referenceQueue) {
    // 1
    super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
    this.key = checkNotNull(key, "key");
    this.name = checkNotNull(name, "name");
  }
}

这是一个自定义的弱引用KeyedWeakReference,里面绑定了检测对象具有的唯一key和name,在注释1中会将弱引用和引用队列ReferenceQueue关联起来,这里有个弱引用的特性,当弱引用referent持有的对象被GC回收之后,JVM就会把这个弱引用加入到与之关联的引用队列referenceQueue中。

2、RefWatcher#ensureGoneAsync

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

在注释1中的watchExecutor就是我们上面在构建RefWatcher传入的参数,它的实现是AndroidWatchExecutor。

AndroidWatchExecutor#execute

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

// postWaitForIdle方法也很清晰,还是切回到主线程去执行waitForIdle方法
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
  mainHandler.post(new Runnable() {
    @Override public void run() {
      waitForIdle(retryable, failedAttempts);
    }
  });
}

private void waitForIdle(final Retryable retryable, final int failedAttempts) {
  // This needs to be called from the main thread.
  // 当主线程空闲的时候才执行queueIdle
  Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    @Override public boolean queueIdle() {
      postToBackgroundWithDelay(retryable, failedAttempts);
      return false;
    }
  });
}

private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
  long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
  // initialDelayMillis的初始值是5
  long delayMillis = initialDelayMillis * exponentialBackoffFactor;
  // 切换的HandlerThread线程执行
  backgroundHandler.postDelayed(new Runnable() {
    @Override public void run() {
      // 执行retryable.run,最终就会执行上面的注释2中的ensureGone方法
      Retryable.Result result = retryable.run();
      // 如果返回的是RETRY,则递归继续执行方法
      if (result == RETRY) {
        postWaitForIdle(retryable, failedAttempts + 1);
      }
    }
  }, delayMillis);
}

RefWatcher#ensureGone

Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
  long gcStartNanoTime = System.nanoTime();
  long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    // 从引用队列查找已经回收的reference,并把reference对应的key从retainedKeys中移除
  removeWeaklyReachableReferences();

    // 如果是调试模式,直接返回RETRY,不进行内存泄漏检测
  if (debuggerControl.isDebuggerAttached()) {
    // The debugger can create false leaks.
    return RETRY;
  }
  // 判断当前引用reference是否已经被回收(retainedKeys是否含有reference.key,有则说明泄漏)
  if (gone(reference)) {
    return DONE;
  }
  // 触发系统进行gc
  gcTrigger.runGc();
  // 再度从引用队列查找已经回收的reference,并把reference对应的key从retainedKeys中移除
  removeWeaklyReachableReferences();
  // 再度判断当前引用reference是否已经被回收(retainedKeys是否含有reference.key,有则说明泄漏)
  if (!gone(reference)) {
    // 没有回收,判定为泄漏
    long startDumpHeap = System.nanoTime();
    long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
        // 1、 调用heapDumper也就是AndroidHeapDumper的dumpHeap方法生成hprof文件
    File heapDumpFile = heapDumper.dumpHeap();
    if (heapDumpFile == RETRY_LATER) {
      // Could not dump the heap.
      return RETRY;
    }
    long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
        // 将hprof文件heapDumpFile和reference.key等信息封装成heapDump
    HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
        .referenceName(reference.name)
        .watchDurationMs(watchDurationMs)
        .gcDurationMs(gcDurationMs)
        .heapDumpDurationMs(heapDumpDurationMs)
        .build();
        // 2、 这个heapdumpListener就是上面说到的ServiceHeapDumpListener
    heapdumpListener.analyze(heapDump);
  }
  return DONE;
}
private boolean gone(KeyedWeakReference reference) {
    // 如果retainedKeys还包含当前reference.key,说明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) {
    // 如果引用队列queue包含某个reference,说明这个reference已经被回收,则调用retainedKeys移除掉reference对应的key
    retainedKeys.remove(ref.key);
  }
}

AndroidHeapDumper#dumpHeap

public File dumpHeap() {
  File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
  ...
  try {
    // 这里是调用Android SDK的API来生成 hprof 文件
    Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
    cancelToast(toast);
    notificationManager.cancel(notificationId);
    return heapDumpFile;
  } catch (Exception e) {
    CanaryLog.d(e, "Could not dump heap");
    // Abort heap dump
    return RETRY_LATER;
  }
}

ServiceHeapDumpListener#analyze

@Override 
public void analyze(@NonNull HeapDump heapDump) {
  checkNotNull(heapDump, "heapDump");
  // 这个listenerServiceClass就是一开始创建ServiceHeapDumpListener传入的DisplayLeakService
  // 方法的作用就是开启一个位于独立进程的HeapAnalyzerService服务使用haha库对hprof文件进行分析,然后把解析结果通过
  // 启动DisplayLeakService服务发送一个通知出去
  HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
public final class HeapAnalyzerService extends ForegroundService
    implements AnalyzerProgressListener {

  private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
  private static final String HEAPDUMP_EXTRA = "heapdump_extra";

  public static void runAnalysis(Context context, HeapDump heapDump,
      Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
    setEnabledBlocking(context, HeapAnalyzerService.class, true);
    setEnabledBlocking(context, listenerServiceClass, true);
    // 启动了HeapAnalyzerService服务,为了避免减慢APP进程或占用内存,这个服务是开启在一个独立进程中的
    Intent intent = new Intent(context, HeapAnalyzerService.class);
    intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
    intent.putExtra(HEAPDUMP_EXTRA, heapDump);
    ContextCompat.startForegroundService(context, intent);
  }

    // HeapAnalyzerService继承了ForegroundService,而ForegroundService是继承了IntentService的,当启动服务的时候
    // 则会调用onHandleIntent方法,在而ForegroundService的onHandleIntent中又执行了这个onHandleIntentInForeground方法
  @Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
    if (intent == null) {
      CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
      return;
    }
    String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
    HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);

        // 1
    HeapAnalyzer heapAnalyzer =
        new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
        heapDump.computeRetainedHeapSize);
    // 2    
    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
  }
}
<service
    android:name="com.squareup.leakcanary.internal.HeapAnalyzerService"
    android:enabled="false"
    android:process=":leakcanary" />
@Override 
protected void onHandleIntent(@Nullable Intent intent) {
  onHandleIntentInForeground(intent);
}

在注释1中新建了一个对象heapAnalyzer,顾名思义,它就是用来分析hprof文件信息的,这里调用了它的checkForLeak方法使用haha库来解析hprof文件。

public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,
    @NonNull String referenceKey,
    boolean computeRetainedSize) {
  ...

  try {
    listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
    // 新建一个内存映射缓存文件buffer
    HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
    // 通过buffer新建一个HprofParser解析器
    HprofParser parser = new HprofParser(buffer);
    listener.onProgressUpdate(PARSING_HEAP_DUMP);
    // 使用HprofParser解析器解析出对应的引用内存快照文件snapshot
    Snapshot snapshot = parser.parse();
    listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
    // 删除了gcRoots中重复的根对象
    deduplicateGcRoots(snapshot);
    listener.onProgressUpdate(FINDING_LEAKING_REF);
    // 找到泄漏引用对象
    Instance leakingRef = findLeakingReference(referenceKey, snapshot);

    // False alarm, weak reference was cleared in between key check and heap dump.
    if (leakingRef == null) {
      return noLeak(since(analysisStartNanoTime));
    }
    // 找到泄漏对象的最短强引用链
    return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
  } catch (Throwable e) {
    return failure(e, since(analysisStartNanoTime));
  }
}
private Instance findLeakingReference(String key, Snapshot snapshot) {
    // 在内存快照中找到所有我们所监测的内存泄漏对象KeyedWeakReference
  ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
  if (refClass == null) {
    throw new IllegalStateException(
        "Could not find the " + KeyedWeakReference.class.getName() + " class in the heap dump.");
  }
  List<String> keysFound = new ArrayList<>();
  for (Instance instance : refClass.getInstancesList()) {
    List<ClassInstance.FieldValue> values = classInstanceValues(instance);
    // 通过反射获取与内存泄漏对象KeyedWeakReference绑定的唯一key字段
    Object keyFieldValue = fieldValue(values, "key");
    if (keyFieldValue == null) {
      keysFound.add(null);
      continue;
    }
    String keyCandidate = asString(keyFieldValue);
    // 判断泄漏对象中的key是否和当前所检测的泄漏引用对象的key相等,相等则返回这个泄漏对象。
    if (keyCandidate.equals(key)) {
      return fieldValue(values, "referent");
    }
    keysFound.add(keyCandidate);
  }
  throw new IllegalStateException(
      "Could not find weak reference with key " + key + " in " + keysFound);
}

再来看注释2,当找到当前检测泄漏对象的最短强引用链之后执行AbstractAnalysisResultService#sendResultToListener。

public static void sendResultToListener(@NonNull Context context,
    @NonNull String listenerServiceClassName,
    @NonNull HeapDump heapDump,
    @NonNull AnalysisResult result) {
  Class<?> listenerServiceClass;
  try {
    // 这个listenerServiceClass指的就是DisplayLeakService
    listenerServiceClass = Class.forName(listenerServiceClassName);
  } catch (ClassNotFoundException e) {
    throw new RuntimeException(e);
  }
  Intent intent = new Intent(context, listenerServiceClass);

    // heapDump包含了hprof文件等信息,result对象是上面提到的泄漏对象引用链AnalysisResult
  File analyzedHeapFile = AnalyzedHeap.save(heapDump, result);
  if (analyzedHeapFile != null) {
    intent.putExtra(ANALYZED_HEAP_PATH_EXTRA, analyzedHeapFile.getAbsolutePath());
  }
  // 启动DisplayLeakService服务,这个服务的作用主要是发出一个泄漏通知
  ContextCompat.startForegroundService(context, intent);
}

DisplayLeakService#onHeapAnalyzed

public class DisplayLeakService extends AbstractAnalysisResultService {

    // DisplayLeakService也继承了IntentService,当启动DisplayLeakService时会调用它的
    // onHandleIntent方法,onHandleIntent又会执行onHeapAnalyzed方法
  @Override
  protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) {
    HeapDump heapDump = analyzedHeap.heapDump;
    AnalysisResult result = analyzedHeap.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);
    }

    if (!shouldSaveResult) {
      String contentTitle = getString(R.string.leak_canary_no_leak_title);
      String contentText = getString(R.string.leak_canary_no_leak_text);
      showNotification(null, contentTitle, contentText);
    } else if (resultSaved) {
      String contentTitle;
      String contentText;
      PendingIntent pendingIntent =
          DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);

      if (result.failure == null) {
        if (result.retainedHeapSize == AnalysisResult.RETAINED_HEAP_SKIPPED) {
          String className = classSimpleName(result.className);
          if (result.excludedLeak) {
            contentTitle = getString(R.string.leak_canary_leak_excluded, className);
          } else {
            contentTitle = getString(R.string.leak_canary_class_has_leaked, className);
          }
        } else {
          String size = formatShortFileSize(this, result.retainedHeapSize);
          String className = classSimpleName(result.className);
          if (result.excludedLeak) {
            contentTitle = getString(R.string.leak_canary_leak_excluded_retaining, className, size);
          } else {
            contentTitle =
                getString(R.string.leak_canary_class_has_leaked_retaining, className, size);
          }
        }
      } else {
        contentTitle = getString(R.string.leak_canary_analysis_failed);
      }
      contentText = getString(R.string.leak_canary_notification_message);
      // 创建一个通知
      showNotification(pendingIntent, contentTitle, contentText);
    } else {
      onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
    }

    afterDefaultHandling(heapDump, result, leakInfo);
  }
    ...
}

DisplayLeakService是一个服务,当保存hprof文件信息和引用泄漏信息成功之后就会发出一个通知供开发者跳转到DisplayLeakActivity中查看泄漏详情。

三、工作机制

  1. RefWatcher.watch()中创建一个自定义弱引用KeyedWeakReference绑定被监控的对象,里面也记录了当前监控对象对应的唯一key。
  2. 然后在主线程空闲的状态下启动后台线程检查弱引用是否被清除,如果没有,调用系统GC,然后再进行检查。
  3. 如果引用还是未被清除,调用系统API把heap内存dump到APP对应的文件系统中的一个hprof文件中,也就是创建一个hprof文件。
  4. 启动HeapAnalyzerService服务,为了避免减慢APP进程和占用内存,这个服务是位于独立进程的,在这个服务会创建一个HeapAnalyzer对象去使用HAHA库解析这个hprof文件。
  5. 在解析这个hprof文件的过程中,HeapAnalyzer会根据唯一的reference key找到这个被监测的泄漏对象KeyedWeakReference,并定位到具体的泄漏引用。
  6. HeapAnalyzer会获取泄漏引用到GC Roots的最短强引用路径,建立导致泄漏的引用链并返回。
  7. 将引用链传递到APP进程的DisplayLeakService服务中,在这个服务会以通知的形式告知开发者发生内存泄漏,开发者可以点击跳转到DisplayLeakActivity中查看具体的泄漏详情。

最后给出两张流程图进行总结

1、LeakCanary#install


2、RefWatcher#watch

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • LeakCanary原理浅析 1.LeakCanary简介 LeakCanary是一个Android和Java的内...
    chewbee阅读 6,796评论 2 38
  • Android内存泄漏一直是困扰我们Android开发者的一个心病,由于开发人员对于知识掌握的不够深入或者代码的不...
    三叶梧桐阅读 1,792评论 0 11
  • 使用 LeakCanary 的集成过程很简单,首先在 build.gradle 文件中添加依赖: 然后实现自己的 ...
    Vinson武阅读 596评论 0 0
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,515评论 16 22
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,559评论 0 11