概述

LeakCanary是用来检测 Java 和 Android 内存泄露的工具。

LeakCanary的原理非常简单。正常情况下一个Activity在onDestroy之后就要销毁,LeakCanary做的就是在一个Activity onDestroy之后将它放在一个WeakReference中,然后将这个WeakReference关联到一个ReferenceQueue。这个ReferenceQueue的作用是,当Activity被回收的时候,系统会将其Activity对应的WeakReference对象加入到ReferenceQueue

然后我们查看ReferenceQueue是否存在WeakReference对象,如果存在说明Activity已经被回收。如果不存在,执行GC操作,再查看是否被回收。如果不存在则证明该Activity泄漏了,之后Dump出heap信息,并用haha这个开源库去分析泄漏路径。

LeakCanary的使用很简单,如下:

public class ExampleApplication extends Application {  @Override public void onCreate() {    super.onCreate();    if (LeakCanary.isInAnalyzerProcess(this)) {      // This process is dedicated to LeakCanary for heap analysis.      // You should not init your app in this process.      return;    }    LeakCanary.install(this);    // Normal app init code...  }}

源码分析

install

走进install():

  public static RefWatcher install(Application application) {    // 添加了监听器,排除了一些不需要观察的类并且完成了创建    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())        .buildAndInstall();  }  /**   * Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+).   */  public RefWatcher buildAndInstall() {    RefWatcher refWatcher = build();    if (refWatcher != DISABLED) {      LeakCanary.enableDisplayLeakActivity(context);      // 将观察者注入进了Application中      ActivityRefWatcher.install((Application) context, refWatcher);    }    return refWatcher;  }  public static void install(Application application, RefWatcher refWatcher) {    new ActivityRefWatcher(application, refWatcher).watchActivities();  }

以上代码所做的主要内容就是创建了一个Activity内存泄露的监听器,注入到了Application中。

watchActivities

然后进入watchActivities()

  public void watchActivities() {    // Make sure you don't get installed twice.    stopWatchingActivities();    // 注册了一个Activity生命周期的监听器    application.registerActivityLifecycleCallbacks(lifecycleCallbacks);  }  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =      new Application.ActivityLifecycleCallbacks() {        @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {        }        @Override public void onActivityStarted(Activity activity) {        }        @Override public void onActivityResumed(Activity activity) {        }        @Override public void onActivityPaused(Activity activity) {        }        @Override public void onActivityStopped(Activity activity) {        }        @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {        }        @Override public void onActivityDestroyed(Activity activity) {          // 当Activity销毁的时候回调refWatcher的watch()函数          ActivityRefWatcher.this.onActivityDestroyed(activity);        }      };  void onActivityDestroyed(Activity activity) {    refWatcher.watch(activity);  }

watch

watch()最后调用的重载函数:

  public void watch(Object watchedReference, String referenceName) {    if (this == DISABLED) {      return;    }    // 判空    checkNotNull(watchedReference, "watchedReference");    checkNotNull(referenceName, "referenceName");    // 记住开始观查的时间    final long watchStartNanoTime = System.nanoTime();    // 随机生成一个key    String key = UUID.randomUUID().toString();    // 加入到一个集合中    retainedKeys.add(key);    // 将Activity包裹成一个弱引用对象    final KeyedWeakReference reference =        new KeyedWeakReference(watchedReference, key, referenceName, queue);    // 检测内存泄露,确保Activity真的被回收    ensureGoneAsync(watchStartNanoTime, reference);  }  private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {    watchExecutor.execute(new Retryable() {      @Override public Retryable.Result run() {        // 这个方法会在Android主线程空闲的时候执行        return ensureGone(reference, watchStartNanoTime);      }    });

ensureGone

进入ensureGone():

  Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {    // 计算从开始观察到gc所用的时间    long gcStartNanoTime = System.nanoTime();    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);    // 清除已经进入ReferenceQueue的弱引用    // 把已被回收的对象的key从retainedKeys移除,剩下的key都是未被回收的对象    removeWeaklyReachableReferences();    if (debuggerControl.isDebuggerAttached()) {      // The debugger can create false leaks.      return RETRY;    }    if (gone(reference)) {      // 如果当前的对象已经弱可达,说明不会造成内存泄漏      return DONE;    }    // 否则手动调用gc,以防止系统并没有回收,误判    gcTrigger.runGc();    // 清除已经进入ReferenceQueue的弱引用    removeWeaklyReachableReferences();    if (!gone(reference)) {      // 内存泄露      long startDumpHeap = System.nanoTime();      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);      // dump出来heap      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));    }    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.    // 在实际垃圾回收之前弱引用就会被加入ReferenceQueue队列    KeyedWeakReference ref;    while ((ref = (KeyedWeakReference) queue.poll()) != null) {      retainedKeys.remove(ref.key);    }  }

其中gcTrigger.runGc();如何保证肯定gc呢:

public interface GcTrigger {  GcTrigger DEFAULT = new GcTrigger() {    @Override public void runGc() {      // Code taken from AOSP FinalizationTest:      // https://android.googlesource.com/platform/libcore/+/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 perfom a gc.      // 触发系统gc操作      Runtime.getRuntime().gc();      // 通过强制限制100毫秒的时间给gc      enqueueReferences();      // 强制调用已经失去引用的对象的finalize方法      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();      }    }  };  void runGc();}

大概意思是system.gc()并不会每次都立即执行,这里从AOSP中拷贝一段GC的代码,从而保证能够进行垃圾清理工作。

后续的如何导出文件,分析,提示内存泄露并不是重点,这里省略了。

疑问

判断引用是否被回收的时候,为什么不直接使用reference.get(),是不是因为这个时候reference.get()的结果有可能不是null,但是已经加入了ReferenceQueue,证明马上就要被回收了。使用reference.get(),更加准确。

参考:
1.深入理解 Android 之 LeakCanary 源码解析
2.LeakCanary源码分析
3.译文:理解Java中的弱引用

更多相关文章

  1. 类和 Json对象
  2. Android(安卓)Resource介绍和使用
  3. Android中文API(144) —— JsonWriter
  4. Android之Handler用法总结
  5. android通过ksoap2对webservice的解析
  6. SlidingMenu和ActionBarSherlock结合做出出色的App布局,Facebook
  7. Android(安卓)View的介绍和使用
  8. Android(安卓)如何开发 Bottom Navigation 风格
  9. Android中,把XML文件转换成Object对象的方法

随机推荐

  1. Android官方资料--A/B System Updates
  2. c#开发android时layout.axml没有智能提示
  3. 解读ExpandableListView源码样式
  4. 初探 Ubuntu下android NDK
  5. 在 actionbar中使用 searchview
  6. Android Studio点击按钮更换背景图片
  7. Android 常用权限
  8. android相关
  9. android scroller类的使用
  10. 安卓横屏设置: