Android主流三方库源码分析(6、深刻理解Leakcanary源码)

前言

成为一名优秀的Android开发,须要一份完备的知识体系,在这里,让咱们一块儿成长为本身所想的那样~。

在Android主流三方库源码分析系列的前几篇文章中,笔者已经对网络、图片、数据库、响应式编程中最热门的第三方开源框架进行了较为深刻地讲解,若是有朋友对这四块感兴趣的话,能够去了解下。本篇,我将会对Android中的内存泄露检测框架Leakcanary的源码流程进行详细地讲解。java

1、原理概述

首先,笔者仔细查看了Leakcanary官方的github仓库,最重要的即是对Leakcanary是如何起做用的(即原理)这一问题进行了阐述,我本身把它翻译成了易于理解的文字,主要分为以下7个步骤:android

  • 一、RefWatcher.watch()建立了一个KeyedWeakReference用于去观察对象。
  • 二、而后,在后台线程中,它会检测引用是否被清除了,而且是否没有触发GC。
  • 三、若是引用仍然没有被清除,那么它将会把堆栈信息保存在文件系统中的.hprof文件里。
  • 四、HeapAnalyzerService被开启在一个独立的进程中,而且HeapAnalyzer使用了HAHA开源库解析了指定时刻的堆栈快照文件heap dump。
  • 五、从heap dump中,HeapAnalyzer根据一个独特的引用key找到了KeyedWeakReference,而且定位了泄露的引用。
  • 六、HeapAnalyzer为了肯定是否有泄露,计算了到GC Roots的最短强引用路径,而后创建了致使泄露的链式引用。
  • 七、这个结果被传回到app进程中的DisplayLeakService,而后一个泄露通知便展示出来了。

官方的原理简单来解释就是这样的:在一个Activity执行完onDestroy()以后,将它放入WeakReference中,而后将这个WeakReference类型的Activity对象与ReferenceQueque关联。这时再从ReferenceQueque中查看是否有没有该对象,若是没有,执行gc,再次查看,仍是没有的话则判断发生内存泄露了。最后用HAHA这个开源库去分析dump以后的heap内存。git

2、简单示例

下面这段是Leakcanary官方仓库的示例代码:github

首先在你项目app下的build.gradle中配置:数据库

dependencies {
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
  releaseImplementation   'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
  // 可选,若是你使用支持库的fragments的话
  debugImplementation   'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
}
复制代码

而后在你的Application中配置:编程

public class WanAndroidApp extends Application {

    private RefWatcher refWatcher;
    
    public static RefWatcher getRefWatcher(Context context) {
        WanAndroidApp application = (WanAndroidApp)     context.getApplicationContext();
        return application.refWatcher;
    }

    @Override public void onCreate() {
      super.onCreate();
      if (LeakCanary.isInAnalyzerProcess(this)) {
        // 1
        return;
      }
      // 2
      refWatcher = LeakCanary.install(this);
    }
}
复制代码

在注释1处,会首先判断当前进程是不是Leakcanary专门用于分析heap内存的而建立的那个进程,即HeapAnalyzerService所在的进程,若是是的话,则不进行Application中的初始化功能。若是是当前应用所处的主进程的话,则会执行注释2处的LeakCanary.install(this)进行LeakCanary的安装。只需这样简单的几行代码,咱们就能够在应用中检测是否产生了内存泄露了。固然,这样使用只会检测Activity和标准Fragment是否发生内存泄漏,若是要检测V4包的Fragment在执行完onDestroy()以后是否发生内存泄露的话,则须要在Fragment的onDestroy()方法中加上以下两行代码去监视当前的Fragment:json

RefWatcher refWatcher = WanAndroidApp.getRefWatcher(_mActivity);
refWatcher.watch(this);
复制代码

上面的RefWatcher其实就是一个引用观察者对象,是用于监测当前实例对象的引用状态的。从以上的分析能够了解到,核心代码就是LeakCanary.install(this)这行代码,接下来,就从这里出发将LeakCanary一步一步进行拆解。缓存

3、源码分析

一、LeakCanary#install()

public static @NonNull RefWatcher install(@NonNull Application application) {
  return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
      .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
      .buildAndInstall();
}
复制代码

在install()方法中的处理,能够分解为以下四步:性能优化

  • 一、refWatcher(application)
  • 二、链式调用listenerServiceClass(DisplayLeakService.class)
  • 三、链式调用excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
  • 四、链式调用buildAndInstall()

首先,咱们来看下第一步,这里调用了LeakCanary类的refWatcher方法,以下所示:微信

public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
  return new AndroidRefWatcherBuilder(context);
}
复制代码

而后新建了一个AndroidRefWatcherBuilder对象,再看看AndroidRefWatcherBuilder这个类。

二、AndroidRefWatcherBuilder

/** A {@link RefWatcherBuilder} with appropriate Android defaults. */
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {

...

    AndroidRefWatcherBuilder(@NonNull Context context) {
        this.context = context.getApplicationContext();
    }

...
}
复制代码

在AndroidRefWatcherBuilder的构造方法中仅仅是将外部传入的applicationContext对象保存起来了。AndroidRefWatcherBuilder是一个适配Android平台的引用观察者构造器对象,它继承了RefWatcherBuilder,RefWatcherBuilder是一个负责创建引用观察者RefWatcher实例的基类构造器。继续看看RefWatcherBuilder这个类。

三、RefWatcherBuilder

public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> {

    ...
    
    public RefWatcherBuilder() {
        heapDumpBuilder = new HeapDump.Builder();
    }

    ...
}
复制代码

在RefWatcher的基类构造器RefWatcherBuilder的构造方法中新建了一个HeapDump的构造器对象。其中HeapDump就是一个保存heap dump信息的数据结构

接着来分析下install()方法中的链式调用的listenerServiceClass(DisplayLeakService.class)这部分逻辑。

四、AndroidRefWatcherBuilder#listenerServiceClass()

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

在这里,传入了一个DisplayLeakService的Class对象,它的做用是展现泄露分析的结果日志,而后会展现一个用于跳转到显示泄露界面DisplayLeakActivity的通知。在listenerServiceClass()这个方法中新建了一个ServiceHeapDumpListener对象,下面看看它内部的操做。

五、ServiceHeapDumpListener

public final class ServiceHeapDumpListener implements HeapDump.Listener {

    ...
    
    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的Class对象和application对象。它的做用就是接收一个heap dump去分析。

而后咱们继续看install()方法链式调用.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())的这部分代码。先看AndroidExcludedRefs.createAppDefaults()。

六、AndroidExcludedRefs#createAppDefaults()

public enum AndroidExcludedRefs {

    ...

    public static @NonNull ExcludedRefs.Builder createAppDefaults() {
      return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
    }
    
    public static @NonNull ExcludedRefs.Builder createBuilder(EnumSet<AndroidExcludedRefs> refs) {
      ExcludedRefs.Builder excluded = ExcludedRefs.builder();
      for (AndroidExcludedRefs ref : refs) {
        if (ref.applies) {
          ref.add(excluded);
          ((ExcludedRefs.BuilderWithParams) excluded).named(ref.name());
        }
      }
      return excluded;
    }
    
    ...
}
复制代码

先来讲下AndroidExcludedRefs这个类,它是一个enum类,它声明了Android SDK和厂商定制的SDK中存在的内存泄露的case,根据AndroidExcludedRefs这个类的类名就可看出这些case都会被Leakcanary的监测过滤掉。目前这个版本是有46种这样的case被包含在内,后续可能会一直增长。而后EnumSet.allOf(AndroidExcludedRefs.class)这个方法将会返回一个包含AndroidExcludedRefs元素类型的EnumSet。Enum是一个抽象类,在这里具体的实现类是通用正规型的RegularEnumSet,若是Enum里面的元素个数大于64,则会使用存储大数据量的JumboEnumSet。最后,在createBuilder这个方法里面构建了一个排除引用的建造器excluded,将各式各样的case分门别类地保存起来再返回出去。

最后,咱们看到链式调用的最后一步buildAndInstall()。

七、AndroidRefWatcherBuilder#buildAndInstall()

private boolean watchActivities = true;
private boolean watchFragments = true;

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

首先,在注释1处,会判断LeakCanaryInternals.installedRefWatcher是否已经被赋值,若是被赋值了,则会抛出异常,警告 buildAndInstall()这个方法应该仅仅只调用一次,在此方法结束时,即在注释6处,该LeakCanaryInternals.installedRefWatcher才会被赋值。再来看注释2处,调用了AndroidRefWatcherBuilder其基类RefWatcherBuilder的build()方法,咱们它是如何建造的。

八、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(defa  ultReachabilityInspectorClasses());
    }

    return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
        heapDumpBuilder);
}
复制代码

能够看到,RefWatcherBuilder包含了如下7个组成部分:

  • 一、excludedRefs : 记录能够被忽略的泄漏路径

  • 二、heapDumpListener : 转储堆信息到hprof文件,并在解析完 hprof 文件后进行回调,最后通知 DisplayLeakService 弹出泄漏提醒

  • 三、debuggerControl : 判断是否处于调试模式,调试模式中不会进行内存泄漏检测。为何呢?由于在调试过程当中可能会保留上一个引用从而致使错误信息上报

  • 四、heapDumper : 堆信息转储者,负责dump 内存泄漏处的 heap 信息到 hprof 文件

  • 五、watchExecutor : 线程控制器,在 onDestroy() 以后而且在主线程空闲时执行内存泄漏检测

  • 六、gcTrigger : 用于 GC,watchExecutor 首次检测到可能的内存泄漏,会主动进行 GC,GC 以后会再检测一次,仍然泄漏的断定为内存泄漏,最后根据heapDump信息生成相应的泄漏引用链

  • 七、reachabilityInspectorClasses : 用于要进行可达性检测的类列表。

最后,会使用建造者模式将这些组成部分构建成一个新的RefWatcher并将其返回。

咱们继续看回到AndroidRefWatcherBuilder的注释3处的 LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true)这行代码。

九、LeakCanaryInternals#setEnabledAsync()

public static void setEnabledAsync(Context context, final Class<?> componentClass,
final boolean enabled) {
  final Context appContext = context.getApplicationContext();
  AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
    @Override public void run() {
      setEnabledBlocking(appContext, componentClass, enabled);
    }
  });
}
复制代码

在这里直接使用了AsyncTask内部自带的THREAD_POOL_EXECUTOR线程池进行阻塞式地显示DisplayLeakActivity。

而后咱们再继续看AndroidRefWatcherBuilder的注释4处的代码。

十、ActivityRefWatcher#install()

public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
    Application application = (Application) context.getApplicationContext();
    // 1
    ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
    
    // 2
    application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
复制代码

能够看到,在注释1处建立一个本身的activityRefWatcher实例,并在注释2处调用了application的registerActivityLifecycleCallbacks()方法,这样就可以监听activity对应的生命周期事件了。继续看看activityRefWatcher.lifecycleCallbacks里面的操做。

private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
    new ActivityLifecycleCallbacksAdapter() {
      @Override public void onActivityDestroyed(Activity activity) {
          refWatcher.watch(activity);
      }
};

public abstract class ActivityLifecycleCallbacksAdapter
implements Application.ActivityLifecycleCallbacks {

}
复制代码

很明显,这里实现并重写了Application的ActivityLifecycleCallbacks的onActivityDestroyed()方法,这样便能在全部Activity执行完onDestroyed()方法以后调用 refWatcher.watch(activity)这行代码进行内存泄漏的检测了

咱们再看到注释5处的FragmentRefWatcher.Helper.install(context, refWatcher)这行代码,

十一、FragmentRefWatcher.Helper#install()

public interface FragmentRefWatcher {

    void watchFragments(Activity activity);

    final class Helper {
    
      private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME =
          "com.squareup.leakcanary.internal.SupportFragmentRefWatcher";
    
      public static void install(Context context, RefWatcher refWatcher) {
        List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
    
        // 1
        if (SDK_INT >= O) {
          fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
        }
    
        // 2
        try {
          Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
          Constructor<?> constructor =
              fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
          FragmentRefWatcher supportFragmentRefWatcher   =
              (FragmentRefWatcher) constructor.newInstance(refWatcher);
          fragmentRefWatchers.add(supportFragmentRefWatcher);
        } catch (Exception ignored) {
        }
    
        if (fragmentRefWatchers.size() == 0) {
          return;
        }
    
        Helper helper = new Helper(fragmentRefWatchers);
    
        // 3
        Application application = (Application) context.getApplicationContext();
        application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
      }
      
    ...
}
复制代码

这里面的逻辑很简单,首先在注释1处将Android标准的Fragment的RefWatcher类,即AndroidOfFragmentRefWatcher添加到新建立的fragmentRefWatchers中。在注释2处使用反射将leakcanary-support-fragment包下面的SupportFragmentRefWatcher添加进来,若是你在app的build.gradle下没有添加下面这行引用的话,则会拿不到此类,即LeakCanary只会检测Activity和标准Fragment这两种状况

debugImplementation   'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
复制代码

继续看到注释3处helper.activityLifecycleCallbacks里面的代码。

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

能够看到,在Activity执行完onActivityCreated()方法以后,会调用指定watcher的watchFragments()方法,注意,这里的watcher可能有两种,但不论是哪种,都会使用当前传入的activity获取到对应的FragmentManager/SupportFragmentManager对象,调用它的registerFragmentLifecycleCallbacks()方法,在对应的onDestroyView()和onDestoryed()方法执行完后,分别使用refWatcher.watch(view)和refWatcher.watch(fragment)进行内存泄漏的检测,代码以下所示。

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

@Override
public void onFragmentDestroyed(FragmentManagerfm, Fragment fragment) {
    refWatcher.watch(fragment);
}
复制代码

注意,下面到真正关键的地方了,接下来分析refWatcher.watch()这行代码。

十二、RefWatcher#watch()

public void watch(Object watchedReference, String referenceName) {
    if (this == DISABLED) {
      return;
    }
    checkNotNull(watchedReference, "watchedReference");
    checkNotNull(referenceName, "referenceName");
    final long watchStartNanoTime = System.nanoTime();
    // 1
    String key = UUID.randomUUID().toString();
    // 2
    retainedKeys.add(key);
    // 3
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);

    // 4
    ensureGoneAsync(watchStartNanoTime, reference);
}
复制代码

注意到在注释1处使用随机的UUID保证了每一个检测对象对应 key 的惟一性。在注释2处将生成的key添加到类型为CopyOnWriteArraySet的Set集合中。在注释3处新建了一个自定义的弱引用KeyedWeakReference,看看它内部的实现。

1三、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标识了一个被检测的WeakReference对象。在注释1处,将弱引用和引用队列 ReferenceQueue 关联起来,若是弱引用reference持有的对象被GC回收,JVM就会把这个弱引用加入到与之关联的引用队列referenceQueue中。即 KeyedWeakReference 持有的 Activity 对象若是被GC回收,该对象就会加入到引用队列 referenceQueue 中

接着咱们回到RefWatcher.watch()里注释4处的ensureGoneAsync()方法。

1四、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);
        }
    });
}
复制代码

在ensureGoneAsync()方法中,在注释1处使用 watchExecutor 执行了注释2处的 ensureGone 方法,watchExecutor 是 AndroidWatchExecutor 的实例。

下面看看watchExecutor内部的逻辑。

1五、AndroidWatchExecutor

public final class AndroidWatchExecutor implements WatchExecutor {

    ...
    
    public AndroidWatchExecutor(long initialDelayMillis)     {
      mainHandler = new Handler(Looper.getMainLooper());
      HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
      handlerThread.start();
      // 1
      backgroundHandler = new Handler(handlerThread.getLooper());
      this.initialDelayMillis = initialDelayMillis;
      maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
    }
    
    @Override public void execute(@NonNull Retryable retryable) {
      // 2
      if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
        waitForIdle(retryable, 0);
      } else {
        postWaitForIdle(retryable, 0);
      }
    }
    
    ...
}
复制代码

在注释1处AndroidWatchExecutor的构造方法中,注意到这里使用HandlerThread的looper新建了一个backgroundHandler,后面会用到。在注释2处,会判断当前线程是不是主线程,若是是,则直接调用waitForIdle()方法,若是不是,则调用postWaitForIdle(),来看看这个方法。

private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
  mainHandler.post(new Runnable() {
    @Override public void run() {
      waitForIdle(retryable, failedAttempts);
    }
  });
}
复制代码

很清晰,这里使用了在构造方法中用主线程looper构造的mainHandler进行post,那么waitForIdle()最终也会在主线程执行。接着看看waitForIdle()的实现。

private void waitForIdle(final Retryable retryable,     final int failedAttempts) {
  Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
    @Override public boolean queueIdle() {
      postToBackgroundWithDelay(retryable, failedAttempts);
      return false;
    }
  });
}
复制代码

这里MessageQueue.IdleHandler()回调方法的做用是当 looper 空闲的时候,会回调 queueIdle 方法,利用这个机制咱们能够实现第三方库的延迟初始化,而后执行内部的postToBackgroundWithDelay()方法。接下来看看它的实现。

private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
  long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts),     maxBackoffFactor);
  // 1
  long delayMillis = initialDelayMillis * exponentialBackoffFactor;
  // 2
  backgroundHandler.postDelayed(new Runnable() {
    @Override public void run() {
      // 3
      Retryable.Result result = retryable.run();
      // 4
      if (result == RETRY) {
        postWaitForIdle(retryable, failedAttempts +   1);
      }
    }
  }, delayMillis);
}
复制代码

先看到注释4处,能够明白,postToBackgroundWithDelay()是一个递归方法,若是result 一直等于RETRY的话,则会一直执行postWaitForIdle()方法。在回到注释1处,这里initialDelayMillis 的默认值是 5s,所以delayMillis就是5s。在注释2处,使用了在构造方法中用HandlerThread的looper新建的backgroundHandler进行异步延时执行retryable的run()方法。这个run()方法里执行的就是RefWatcher的ensureGoneAsync()方法中注释2处的ensureGone()这行代码,继续看它内部的逻辑。

1六、RefWatcher#ensureGone()

Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime -     watchStartNanoTime);

    // 1
    removeWeaklyReachableReferences();

    // 2
    if (debuggerControl.isDebuggerAttached()) {
      // The debugger can create false leaks.
      return RETRY;
    }
    
    // 3
    if (gone(reference)) {
      return DONE;
    }
    
    // 4
    gcTrigger.runGc();
    removeWeaklyReachableReferences();
    
    // 5
    if (!gone(reference)) {
      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;
}
复制代码

在注释1处,执行了removeWeaklyReachableReferences()这个方法,接下来分析下它的含义。

private void removeWeaklyReachableReferences() {
    KeyedWeakReference ref;
    while ((ref = (KeyedWeakReference) queue.poll()) != null) {
        retainedKeys.remove(ref.key);
    }
}
复制代码

这里使用了while循环遍历 ReferenceQueue ,并从 retainedKeys中移除对应的Reference。

再看到注释2处,当Android设备处于debug状态时,会直接返回RETRY进行延时重试检测的操做。在注释3处,咱们看看gone(reference)这个方法的逻辑。

private boolean gone(KeyedWeakReference reference) {
    return !retainedKeys.contains(reference.key);
}
复制代码

这里会判断 retainedKeys 集合中是否还含有 reference,若没有,证实已经被回收了,若含有,可能已经发生内存泄露(或Gc尚未执行回收)。前面的分析中咱们知道了 reference 被回收的时候,会被加进 referenceQueue 里面,而后咱们会调用removeWeaklyReachableReferences()遍历 referenceQueue 移除掉 retainedKeys 里面的 refrence

接着咱们看到注释4处,执行了gcTrigger的runGc()方法进行垃圾回收,而后使用了removeWeaklyReachableReferences()方法移除已经被回收的引用。这里咱们再深刻地分析下runGc()的实现。

GcTrigger DEFAULT = new GcTrigger() {
    @Override public void runGc() {
      // Code taken from AOSP FinalizationTest:
      // https://android.googlesource.com/platform/libc  ore/+/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 perform a gc.
      Runtime.getRuntime().gc();
      enqueueReferences();
      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();
      }
    }
};
复制代码

这里并无使用System.gc()方法进行回收,由于system.gc()并不会每次都执行。而是从AOSP中拷贝一段GC回收的代码,从而相比System.gc()更可以保证垃圾回收的工做

最后咱们分析下注释5处的代码处理。首先会判断activity是否被回收,若是尚未被回收,则证实发生内存泄露,进行if判断里面的操做。在里面先调用堆信息转储者heapDumper的dumpHeap()生成相应的 hprof 文件。这里的heapDumper是一个HeapDumper接口,具体的实现是AndroidHeapDumper。咱们分析下AndroidHeapDumper的dumpHeap()方法是如何生成hprof文件的。

public File dumpHeap() {
    File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();

    if (heapDumpFile == RETRY_LATER) {
        return RETRY_LATER;
    }

    ...
    
    try {
      Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
      ...
      
      return heapDumpFile;
    } catch (Exception e) {
      ...
      // Abort heap dump
      return RETRY_LATER;
    }
}
复制代码

这里的核心操做就是调用了Android SDK的API Debug.dumpHprofData() 来生成 hprof 文件

若是这个文件等于RETRY_LATER则表示生成失败,直接返回RETRY进行延时重试检测的操做。若是不等于的话,则表示生成成功,最后会执行heapdumpListener的analyze()对新建立的HeapDump对象进行泄漏分析。由前面对AndroidRefWatcherBuilder的listenerServiceClass()的分析可知,heapdumpListener的实现 就是ServiceHeapDumpListener,接着看到ServiceHeapDumpListener的analyze方法。

1七、ServiceHeapDumpListener#analyze()

@Override public void analyze(@NonNull HeapDump heapDump) {
    checkNotNull(heapDump, "heapDump");
    HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
复制代码

能够看到,这里执行了HeapAnalyzerService的runAnalysis()方法,为了不下降app进程的性能或占用内存,这里将HeapAnalyzerService设置在了一个独立的进程中。接着继续分析runAnalysis()方法里面的处理。

public final class HeapAnalyzerService extends ForegroundService
implements AnalyzerProgressListener {

    ...

    public static void runAnalysis(Context context, HeapDump heapDump,
    Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
        ...
        
        ContextCompat.startForegroundService(context, intent);
    }

    ...
    
    @Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
        ...

        // 1
        HeapAnalyzer heapAnalyzer =
            new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);

        // 2
        AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
        heapDump.computeRetainedHeapSize);
        
        // 3
        AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
    }
        ...
}
复制代码

这里的HeapAnalyzerService实质是一个类型为IntentService的ForegroundService,执行startForegroundService()以后,会回调onHandleIntentInForeground()方法。注释1处,首先会新建一个HeapAnalyzer对象,顾名思义,它就是根据RefWatcher生成的heap dumps信息来分析被怀疑的泄漏是不是真的。在注释2处,而后会调用它的checkForLeak()方法去使用haha库解析 hprof文件,以下所示:

public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,
  @NonNull String referenceKey,
  boolean computeRetainedSize) {
    ...
    
    try {
    listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
    // 1
    HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
  
    // 2
    HprofParser parser = new HprofParser(buffer);
    listener.onProgressUpdate(PARSING_HEAP_DUMP);
    Snapshot snapshot = parser.parse();
  
    listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
    // 3
    deduplicateGcRoots(snapshot);
    listener.onProgressUpdate(FINDING_LEAKING_REF);
  
    // 4
    Instance leakingRef = findLeakingReference(referenceKey, snapshot);

    // 5
    if (leakingRef == null) {
        return noLeak(since(analysisStartNanoTime));
    }
  
    // 6
    return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
    } catch (Throwable e) {
    return failure(e, since(analysisStartNanoTime));
    }
}
复制代码

在注释1处,会新建一个内存映射缓存文件buffer。在注释2处,会使用buffer新建一个HprofParser解析器去解析出对应的引用内存快照文件snapshot。在注释3处,为了减小在Android 6.0版本中重复GCRoots带来的内存压力的影响,使用deduplicateGcRoots()删除了gcRoots中重复的根对象RootObj。在注释4处,调用了findLeakingReference()方法将传入的referenceKey和snapshot对象里面全部类实例的字段值对应的keyCandidate进行比较,若是没有相等的,则表示没有发生内存泄漏,直接调用注释5处的代码返回一个没有泄漏的分析结果AnalysisResult对象。若是找到了相等的,则表示发生了内存泄漏,执行注释6处的代码findLeakTrace()方法返回一个有泄漏分析结果的AnalysisResult对象。

最后,咱们来分析下HeapAnalyzerService中注释3处的AbstractAnalysisResultService.sendResultToListener()方法,很明显,这里AbstractAnalysisResultService的实现类就是咱们刚开始分析的用于展现泄漏路径信息的DisplayLeakService对象。在里面直接建立一个由PendingIntent构建的泄漏通知用于供用户点击去展现详细的泄漏界面DisplayLeakActivity。核心代码以下所示:

public class DisplayLeakService extends AbstractAnalysisResultService {

    @Override
    protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) {
    
        ...
        
        boolean resultSaved = false;
        boolean shouldSaveResult = result.leakFound || result.failure != null;
        if (shouldSaveResult) {
            heapDump = renameHeapdump(heapDump);
            // 1
            resultSaved = saveResult(heapDump, result);
        }
        
        if (!shouldSaveResult) {
            ...
            showNotification(null, contentTitle, contentText);
        } else if (resultSaved) {
            ...
            // 2
            PendingIntent pendingIntent =
                DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);

            ...
            
            showNotification(pendingIntent, contentTitle, contentText);
        } else {
             onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
        }
    
    ...
}

@Override protected final void onAnalysisResultFailure(String failureMessage) {
    super.onAnalysisResultFailure(failureMessage);
    String failureTitle = getString(R.string.leak_canary_result_failure_title);
    showNotification(null, failureTitle, failureMessage);
}
复制代码

能够看到,只要当分析的堆信息文件保存成功以后,即在注释1处返回的resultSaved为true时,才会执行注释2处的逻辑,即建立一个供用户点击跳转到DisplayLeakActivity的延时通知。最后给出一张源码流程图用于回顾本篇文章中LeakCanary的运做流程:

image

4、总结

性能优化一直是Android中进阶和深刻的方向之一,而内存泄漏一直是性能优化中比较重要的一部分,Android Studio自身提供了MAT等工具去分析内存泄漏,可是分析起来比较耗时耗力,于是才诞生了LeakCanary,它的使用很是简单,可是通过对它的深刻分析以后,才发现,简单的API后面每每藏着许多复杂的逻辑处理,尝试去领悟它们,你可能会发现不同的世界

参考连接:

一、LeakCanary V1.6.2 源码

二、一步步拆解 LeakCanary

三、深刻理解 Android 之 LeakCanary 源码解析

Contanct Me

● 微信:

欢迎关注个人微信:bcce5360

● 微信群:

微信群若是不能扫码加入,麻烦你们想进微信群的朋友们,加我微信拉你进群。

● QQ群:

2千人QQ群,Awesome-Android学习交流群,QQ群号:959936182, 欢迎你们加入~

About me

很感谢您阅读这篇文章,但愿您能将它分享给您的朋友或技术群,这对我意义重大。

但愿咱们能成为朋友,在 Github掘金上一块儿分享知识。

相关文章
相关标签/搜索