LeakCanary看这一篇文章就够了

作者:潇湘夜雨123 https://www.jianshu.com/p/8c06c9c9317c 本文由作者投稿并原创发布

LeakCanary简单介绍

LeakCanary是Square公司基于MAT开源的一个内存泄漏检测工具,在发生内存泄漏的时候LeakCanary会自动显示泄漏信息。

LeakCanary简单使用

配置build.gradle:

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

自定义Application

public class ExampleApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        setupLeakCanary();
    }

    protected void setupLeakCanary() {
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
        }
        LeakCanary.install(this);
    }

如果当前的进程是用来给LeakCanary 进行堆分析的则return,否则会执行LeakCanary的install方法。这样我们就可以使用LeakCanary了,如果检测到某个Activity 有内存泄露,LeakCanary 就会给出提示。

LeakCanary原理

LeakCanary原理是将检测的对象放入弱引用中并且关联到引用队列中,查看引用队列中是否存在引用,如果发现泄露,dump出信息进行分析。 先从LeakCanary的install入手。

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

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

install方法用于创建AndroidRefWatcherBuilder类,最终调用buildAndInstall方法。

public RefWatcher buildAndInstall() {
    if (LeakCanaryInternals.installedRefWatcher != null) {
      throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
    }
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
      if (watchActivities) {
        ActivityRefWatcher.install(context, refWatcher);
      }
      if (watchFragments) {
        FragmentRefWatcher.Helper.install(context, refWatcher);
      }
    }
    LeakCanaryInternals.installedRefWatcher = refWatcher;
    return refWatcher;
  }

/** Creates a {@link RefWatcher}. */
public final RefWatcher build() {
    if (isDisabled()) {
      return RefWatcher.DISABLED;
}

buildAndInstall方法用于监听activity和fragment内存泄露,通过build方法创建RefWatcher从而创建ActivityRefWatcher和FragmentRefWatcher。

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

首先进入监听activity类的ActivityRefWatcher类install中。 给Application注册ActivityLifecycleCallbacks,让activity中的onDestroy方法关联到refWatcher.watch中。 Application通过ActivityLifecycleCallbacks使用接口提供了一套回调方法, 对Activity的生命周期事件检测。ActivityLifecycleCallbacks使用要求API 14+ 。

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

      if (SDK_INT >= O) {
        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);
        fragmentRefWatchers.add(supportFragmentRefWatcher);
      } catch (Exception ignored) {
      }

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

      Helper helper = new Helper(fragmentRefWatchers);

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

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

再进入监听fragment的FragmentRefWatcher中,其内部类Helper将实现FragmentRefWatcher接口的SupportFragmentRefWatcher和AndroidOFragmentRefWatcher加入到fragmentRefWatchers数组中。 因为支持v4包下的fragment在另一个moudle中,所以通过反射的方式创建对象。 最后注册ActivityLifecycleCallbacks监听Activity的onCreated方法。

  private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
      new FragmentManager.FragmentLifecycleCallbacks() {
        @Override
        public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
          refWatcher.watch(fragment);
        }
      };
  @Override public void watchFragments(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
  }

因为SupportFragmentRefWatcher和AndroidOFragmentRefWatcher是FragmentRefWatcher的实现类。最终会回调实现类的watchFragments方法,最终fragment会注册FragmentLifecycleCallbacks方法监听fragment。 activity和fragment最终都会调用RefWatcher中的watch方法,所以要先进入RefWatcher类中 看具体实现。

public final class RefWatcher {
  public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();

  private final WatchExecutor watchExecutor;
  private final DebuggerControl debuggerControl;
  private final GcTrigger gcTrigger;
  private final HeapDumper heapDumper;
  private final Set<String> retainedKeys;
  private final ReferenceQueue<Object> queue;
  private final HeapDump.Listener heapdumpListener;
  private final ExcludedRefs excludedRefs;
  private final boolean computeRetainedHeapSize;
}

WatchExecutor watchExecutor:查找检测内存泄露的对象 DebuggerControl debuggerControl:检测当前是否正在调试中 GcTrigger gcTrigger:调用gc方法 HeapDumper heapDumper:dump内存泄露产生的文件 SetretainedKeys:存储引用key(待检测或已经产生泄露) ReferenceQueue queue:引用队列,存储待检测的弱引用 HeapDump.Listener heapdumpListener:HeapDumper的回调 ExcludedRefs excludedRefs:排除系统引起的内存泄露 boolean computeRetainedHeapSize:检测泄露时是否计算堆的大小,默认为false

查看RefWatcher中的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);
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);

    ensureGoneAsync(watchStartNanoTime, reference);
  }

首先对retainedKeys添加一个唯一的key,并且将待检测的watchedReference加入转化为弱引用加入到引用队列中,开启异步线程ensureGoneAsync分析是否泄露。

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

在ensureGoneAsync方法中,这里通过WatchExecutor接口的实现类AndroidWatchExecutor的,最后handler加入到消息队列中。

  @SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
  Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);//gc时间到调用watch方法的时间差

    removeWeaklyReachableReferences();//清除到达引用队列中的弱引用

    if (debuggerControl.isDebuggerAttached()) {//判断是否为调试阶段
      // The debugger can create false leaks.
      return RETRY;
    }
    if (gone(reference)) {//判断检测对象是否达到引用队列
      return DONE;
    }
    gcTrigger.runGc();//调用gc回收
    removeWeaklyReachableReferences();//再次清除到达引用队列中的弱引用
       //再次判断检测对象是否达到引用队列如果未到达,产生heap文件。
    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.Durations durations =
          new HeapDump.Durations(watchDurationMs, gcDurationMs, heapDumpDurationMs);
      heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs,
              computeRetainedHeapSize, durations));
    }
    return DONE;
  }

在ensureGone方法中,会清除引用队列中的弱引用,接着检测对象是否达到引用队列,然后调用gcTrigger.runGc进行gc回收,然后会再次清除引用队列中的弱引用。最终会调用heapdumpListener.analyze方法进行回调。

public final class ServiceHeapDumpListener implements HeapDump.Listener 
  @Override public void analyze(HeapDump heapDump) {
    checkNotNull(heapDump, "heapDump");
    HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
  }

heapdumpListener是HeapDump的内部接口。在ServiceHeapDumpListener进行实现然后会调用 HeapAnalyzerService.runAnalysis方法。

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

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

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

    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
        heapDump.computeRetainedHeapSize);
    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
  }}

进入HeapAnalyzerService.runAnalysis方法可以看出会启动HeapAnalyzerService,HeapAnalyzerService是ForegroundService的子类。最终会调用onHandleIntentInForeground方法。 onHandleIntentInForeground方法会首先判断intent是否为空,然后调用heapAnalyzer.checkForLeak进行文件分析。最后调用AbstractAnalysisResultService.sendResultToListener进行回调显示结果。 进入HeapAnalyzer这个类中的checkForLeak方法 。

   public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey,
      boolean computeRetainedSize) {
    long analysisStartNanoTime = System.nanoTime();

    if (!heapDumpFile.exists()) {
      Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
      return failure(exception, since(analysisStartNanoTime));
    }

    try {
      listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
       //将heap文件封装成MemoryMappedFileBuffer
      HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
      //创建hprof解析器,解析hprof文件
      HprofParser parser = new HprofParser(buffer);
      listener.onProgressUpdate(PARSING_HEAP_DUMP);
      Snapshot snapshot = parser.parse();//进行解析
      listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
      deduplicateGcRoots(snapshot);//去除重复的内容
      listener.onProgressUpdate(FINDING_LEAKING_REF);
       //检测解析的结果是否存在referenceKey的引用
      Instance leakingRef = findLeakingReference(referenceKey, snapshot);
      //根据leakingRef的结果调用noLeak或者寻找路径
      // 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));
    }
  }

首先将heap文件封装成HprofParser,然后将hprof解析成内存快照,并且去掉重复的引用。最后根据内存快照找出泄露对象的最短路径。

原文发布于微信公众号 - 刘望舒(liuwangshuAndroid)

原文发表时间:2018-06-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序猿DD

Spring框架中的设计模式(四)​

本文是Spring框架中使用的设计模式第四篇。本文将在此呈现出新的3种模式。一开始,我们会讨论2种结构模式:适配器和装饰器。在第三部分和最后一部分,我们将讨论单...

3916
来自专栏Flutter入门

Weex是如何在Android客户端上跑起来的

Weex可以通过自己设计的DSL,书写.we文件或者.vue文件来开发界面,整个页面书写分成了3段,template、style、script,借鉴了成熟的MV...

4275
来自专栏逸鹏说道

我这么玩Web Api(二)

数据验证,全局数据验证与单元测试 目录 一、模型状态 - ModelState 二、数据注解 - Data Annotations 三、自定义数据注解 四、全局...

5226
来自专栏大内老A

通过一个模拟程序让你明白ASP.NET MVC是如何运行的

ASP.NET MVC的路由系统通过对HTTP请求的解析得到表示Controller、Action和其他相关的数据,并以此为依据激活Controller对象,调...

2916
来自专栏恰童鞋骚年

ASP.Net请求处理机制初步探索之旅 - Part 5 ASP.Net MVC请求处理流程

开篇:上一篇我们了解了在WebForm模式下一个Page页面的生命周期,它经历了初始化Init、加载Load以及呈现Render三个重要阶段,其中构造了页面控件...

1393
来自专栏菩提树下的杨过

java学习:weblogic下JNDI及JDBC连接测试(weblogic环境)

JNDI的专业解释,大家自行去网络搜索吧,这里就不啰嗦了。 单纯从使用角度看,可以简称把它看成一个key-value的“哈希资源”容器。给定一个string类型...

2849
来自专栏DOTNET

asp.net web api 下载之断点续传

一、基本思想 利用 HTTP 请求的Range标头值,来向服务端传递请求数据的开始位置和结束位置。服务端获得这两个参数后,将指定范围内的数据传递给客户端。当客户...

47312
来自专栏大内老A

在ASP.NET MVC中如何应用多个相同类型的ValidationAttribute?

ASP.NET MVC采用System.ComponentModel.DataAnnotations提供的元数据验证机制对Model实施验证,我们可以在Mode...

2085
来自专栏Android 开发学习

JsBridge 源码分析

1733
来自专栏cmazxiaoma的架构师之路

通用Mapper和PageHelper插件 学习笔记

9753

扫码关注云+社区

领取腾讯云代金券