LeakCanary 使用说明

LeakCanary 使用说明

LeakCanary 作为最简单直接的内存泄漏检测工具,非常受欢迎,github 上已经达到了 1.9 W star。官方地址如下:https://github.com/square/leakcanary

使用说明:

  1. 在debug 版本-设置-环境设置-LeakCanary Setting 查看是否打开了 LeakCanary。

  2. 执行你的日常开发和自测,目前支持 Activity 泄漏 和 window 泄漏检测。

  3. 每次Window 退出时,会进行泄漏检测。如果存在泄漏,会静止进程,dump 出 heap。如图1

  4. 在通知栏能看到泄漏的 WIndow,如图2,或者点击桌面图标-极速版内存泄漏,可以看到更多的泄漏信息,如图3

  5. 查看泄漏信息,根据具体的引用情况,解决内存泄漏。

集成说明

极速版已经集成 LeakCanary 用于检测 Window 泄漏,具体植入代码:

gradle.build

debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'

也就是意味着,debug 和 release 依赖的包不同,而发布 release 包时,依赖 no-op 这个库,里面只有,两个类,只有五个方法,所以直接发布 release 包,对包大小基本没有影响。

原理简介

LeakCanary 的 使用代码如下:

public class ExampleApplication extends Application {

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

  private RefWatcher refWatcher;

  @Override public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // 判断是否和 LeakCanary 初始化同一进程
      return;
    }
    refWatcher = LeakCanary.install(this);//获取一个 Watcher
  }
}

检测 Activity 泄漏的话,由于 LeakCanary install 的时候,会通过注入一个 Application.ActivityLifecycleCallbacks 去监听 Activity 的生命周期,所以,无限我们自己监听。

public void watchActivities() {
  // Make sure you don't get installed twice.
  stopWatchingActivities();
  application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new Application.ActivityLifecycleCallbacks() {
        @Override public void onActivityDestroyed(Activity activity) {
          ActivityRefWatcher.this.onActivityDestroyed(activity);
        }
      };

比如你需要检测一个 Window 泄漏,你就在 Window 的 onDestory() 方法里面:

  @Override public void onDestroy() {
    super.onDestroy();
    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
    refWatcher.watch(this);
  }

其实就是通过软引用,持有 this 当前对象,然后会触发一次 gc ,然后在检测当前this 是否为 null。

RefWatcher.java

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

  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);
      }
    });
  }

  Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    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();//关键点2 触发 GC
    removeWeaklyReachableReferences();//关键点 3 移除被回收的对象
    if (!gone(reference)) {//关键点 4 ,查看当前引用是否泄漏,如果泄漏,在后台service dump heap 
      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);
      heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
              gcDurationMs, heapDumpDurationMs));
    }
    return DONE;
  }

  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);
    }
  }

结合代码,给出一些相关解释:

  1. 关键点 1,弱引用和引用队列 ReferenceQueue 联合使用时,如果弱引用持有的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。即 KeyedWeakReference 持有的 Activity 对象如果被垃圾回收,该对象就会加入到引用队列 queue 中。
  2. 关键点2,这里会执行 Runtime.getRuntime().gc(); 触发 GC
  3. 关键点3,通过查看 queue,也就是 GC 之后,回收对象的队列,是否包含当前对象,如果包含,则将对应的 key,从 retainedKeys Set 里面移除
  4. 如果 retainedKeys Set 里面依旧包含该 key,则在 DisplayLeakService 会延迟一定时间,收集 Heap 信息。

你可能感兴趣的:(android,性能模块)