LeakCanary内存泄漏框架源码解析

作者: 小村医 | 来源:发表于2019-09-21 11:58 被阅读0次

LeakCanary.install(this); 开始
下面我们来看下它做了些什么

1、首先创建一个RefWatcher,启动一个ActivityRefWatcher

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

这里主要是创建一个RefWatcher对象,其中有两个对象需要说明一下:

  • DisplayLeakService:主要是用来展示内存泄漏的通知
  • AndroidExcludedRefs:一个枚举类,里面定义了一下Android SDK内部的内存泄漏,当检测到系统的内存泄漏时会排除掉

再看一下buildAndInstall()方法的内部实现

 public RefWatcher buildAndInstall() {
    //创建一个RefWatcher对象
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
      //开启展示内存泄漏的activity
      LeakCanary.enableDisplayLeakActivity(context);
      // 开始检测Activity引用
      ActivityRefWatcher.install((Application) context, refWatcher);
    }
    return refWatcher;
  }

2、通过ActivityLifecycleCallbacks关联Activity的onDestory生命周期

Activity生命周期的关联是通过上一步中调用ActivityRefWatcher.install()关联的,具体怎么实现的下面看一下源码:

public static void install(Application application, RefWatcher refWatcher) {
    new ActivityRefWatcher(application, refWatcher).watchActivities();
  }
//监控activity生命周期
public void watchActivities() {
    // Make sure you don't get installed twice.
    stopWatchingActivities();
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
  }
// activity生命周期回调
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) {
      ActivityRefWatcher.this.onActivityDestroyed(activity);
    }
  };

3、在线程池中开启线程分析内存泄漏

Activity销毁后会调用RefWatcherwatch方法开始检测Activity
在看watch的实现之前先卡一下RefWatcher里面定义的变量,就能大概了解到RefWatcher主要做什么工作

public final class RefWatcher {
  // 执行内存检测的线程池
  private final WatchExecutor watchExecutor;
  // 查询是否在debug调试中,如果再调试中不会执行内存泄漏的检测
  private final DebuggerControl debuggerControl;
  // 触发gc垃圾回收
  private final GcTrigger gcTrigger;
  // dump内存的堆文件
  private final HeapDumper heapDumper;
  // 持有待检测的和已经内存泄漏的对象的key
  private final Set<String> retainedKeys;
  // 创建WeakReference时传入的引用队列,主要是用来判断弱引用持有的对象是否被gc回收,gc回收后会把弱引用添加到队列中
  private final ReferenceQueue<Object> queue;
  // 监听产生heap文件的回调
  private final HeapDump.Listener heapdumpListener;
  // 需要排除的一些系统内存泄漏
  private final ExcludedRefs excludedRefs;
}

看一下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();
    // 将要观察的类的key添加到set中
    retainedKeys.add(key);
    // 创建一个弱引用并将对象和key进行关联
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);

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

4、判断对象是否被回收

判断一个对象是否被回收主要实在ensureGone是完成的

  Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    // 删除已经被回收的弱引用
    removeWeaklyReachableReferences();
    // debug调试中返回
    if (debuggerControl.isDebuggerAttached()) {
      // The debugger can create false leaks.
      return RETRY;
    }
    // 已经回收,返回
    if (gone(reference)) {
      return DONE;
    }
    // 触发gc
    gcTrigger.runGc();
    removeWeaklyReachableReferences();
    if (!gone(reference)) {
      //dump内存快照
      File heapDumpFile = heapDumper.dumpHeap();
      if (heapDumpFile == RETRY_LATER) {
        // Could not dump the heap.
        return RETRY;
      }
      // 分析内存快照
      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() {

    KeyedWeakReference ref;
    while ((ref = (KeyedWeakReference) queue.poll()) != null) {
      retainedKeys.remove(ref.key);
    }
  }

5 checkforLeak

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

    if (!heapDumpFile.exists()) {
      Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
      return failure(exception, since(analysisStartNanoTime));
    }
    try {
      HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
      // 解析 hprof文件
      HprofParser parser = new HprofParser(buffer);
      // 转成 snapshot快照
      Snapshot snapshot = parser.parse();
      //去除重复的内存泄漏
      deduplicateGcRoots(snapshot);
      // 根据key查询解析结果是否有 我们需要的对象
      Instance leakingRef = findLeakingReference(referenceKey, snapshot);

      // 在heap dump过程中引用被回收
      if (leakingRef == null) {
        return noLeak(since(analysisStartNanoTime));
      }
      // 找出泄漏的路径
      return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
    } catch (Throwable e) {
      return failure(e, since(analysisStartNanoTime));
    }
  }
  1. 把.hprof转为Snapshot
  2. 优化gcroots
  3. 找出泄漏的对象和泄漏对象的最短路径

6、findLeakingReference和 findLeakTrace

findLeakingReference找出内存泄漏的引用

  1. 在Snapshot中找到弱引用
  2. 遍历KeyedWeakReference这个类的所有实例
  3. 如果key值和最开始定义的key值相同,那么返回这个泄漏对象
  private Instance findLeakingReference(String key, Snapshot snapshot) {
    //从内存快照找查找泄漏的弱引用对象
    ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
    List<String> keysFound = new ArrayList<>();
    for (Instance instance : refClass.getInstancesList()) {
      List<ClassInstance.FieldValue> values = classInstanceValues(instance);
      String keyCandidate = asString(fieldValue(values, "key"));
      // key 值相等表示找到我们需要的内存泄漏的对象 
      if (keyCandidate.equals(key)) {
        return fieldValue(values, "referent");
      }
      keysFound.add(keyCandidate);
    }
  }

findLeakTrace找到最短路径作为反馈结果反馈出来

 private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
      Instance leakingRef) {
    // 通过snapshot查找最短路径
    ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
    ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);

    // False alarm, no strong reference path to GC Roots.
    if (result.leakingNode == null) {
      return noLeak(since(analysisStartNanoTime));
    }
    //生成内存泄漏的调用栈
    LeakTrace leakTrace = buildLeakTrace(result.leakingNode);

    String className = leakingRef.getClassObj().getClassName();

    // Side effect: computes retained size.
    snapshot.computeDominators();

    Instance leakingInstance = result.leakingNode.instance;
    // 计算内存泄漏的空间大小
    long retainedSize = leakingInstance.getTotalRetainedSize();

    // TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
    if (SDK_INT <= N_MR1) {
      retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
    }

    return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
        since(analysisStartNanoTime));
  }

6、如何dump内存快照

dump hprof文件

public File dumpHeap() {
   ......
   //调用系统的dumpHprofData方法
   Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
   ......
}

相关文章

  • LeakCanary源码解析

    LeakCanary源码解析 前言 对于内存泄漏的检测,基于MAT起点较高,所以一般我们都使用LeakCanary...

  • LeakCanary内存泄漏框架源码解析

    从 LeakCanary.install(this); 开始下面我们来看下它做了些什么 1、首先创建一个RefWa...

  • 2020-12-09

    LeakCanary工作原理和源码解析 内存泄漏一直以来就是Android APP需要着重解决的点,而LeakCa...

  • leakcanery2源码

    LeakCanary是Android上用于检查内存泄漏的工具,LeakCanary大大减少因内存泄漏导致的内存溢出...

  • 【Android测试】内存泄漏检测 LeakCanary

    什么是内存泄漏和内存溢出?内存泄漏有什么危害?LeakCanary检测内存泄漏? 内存泄漏(Memory Leak...

  • LeakCanary源码解析

    LeakCanary源码解析 内存泄露 今天来讲解一下老生常谈的问题了,内存泄露以及讲解LeakCanary是如果...

  • LeakCanary2.0简析

    LeakCanary是一个Android中检查内存泄漏的框架,其使用方法简单,自动检测内存泄漏问题并且还能显示出泄...

  • LeakCanary和常见内存泄漏场景

    LeakCanary和常见内存泄漏场景 一. LeakCanary介绍 1. 介绍 2. 用法 二. 常见泄漏方式...

  • LeakCanary源码笔记

    LeakCanary 由Square开源的一款轻量级第三方内存泄漏检测工具为什么需要LeakCanary框架:性能...

  • Android内存分析与监控

    1.查看GC日志 2,使用Profiler分析内存 3,使用LeakCanary监控内存泄漏 LeakCanary...

网友评论

    本文标题:LeakCanary内存泄漏框架源码解析

    本文链接:https://www.haomeiwen.com/subject/yjgsuctx.html