Android开发Android开发经验谈Android技术知识

LeakCanary内存泄漏框架源码解析

2019-09-21  本文已影响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对象,其中有两个对象需要说明一下:

再看一下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());
   ......
}
上一篇 下一篇

猜你喜欢

热点阅读