LeakCanary内存泄漏原理分析

基本使用

  • 1.5.3版本使用简介

  • 添加依赖

debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'

  • 自定义Application
class App:Application() {
    override fun onCreate() {
        super.onCreate()
        LeakCanary.install(this)
    }
}
  • 2.0版本呢使用简介

  • 添加依赖

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7.1'

  • 自定义Application
class App:Application() {
    override fun onCreate() {
        super.onCreate()
        var config = AppWatcher.config.copy(watchFragmentViews = false)
        AppWatcher.config = config
    }
}

1.5.4版本原理分析

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


public RefWatcher buildAndInstall() {
  RefWatcher refWatcher = build();
  if (refWatcher != DISABLED) {
    LeakCanary.enableDisplayLeakActivity(context);
    ActivityRefWatcher.install((Application) context, refWatcher);
  }
  return refWatcher;
}
  • install方法执行初始化工作,它在Application的onCreate方法中调用。
  • refWatcher方法

        创建并返回了AndroidRefWatcherBuilder类对象。AndroidRefWatcherBuilder的核心工作是初始化,并构建RefWatcher类。RefWatcher是LeakCanary的一个核心类,核心工作是检查时候发生内存泄漏。

  • listenerServiceClass方法

        分析结果监听。通过ServiceHeapDumpListener包装了listenerServiceClass类

入参:DisplayLeakService类。它继承自AbstractAnalysisResultService。主要工作是将将内存泄漏的信息,以前台通知形式告诉开发者。

  • excludeRefs方法

        排除系统bug

  • buildAndInstall方法

        buildAndInstall方法做了两件事情,一、创建了RefWatcher对象,二、关联RefWatcher和Activity的生命周期。具体如下1.1和1.2代码。

1.1、AndroidRefWatcherBuilder.buildAndInstall

public RefWatcher buildAndInstall() {
  RefWatcher refWatcher = build();
  if (refWatcher != DISABLED) {
    LeakCanary.enableDisplayLeakActivity(context);
    ActivityRefWatcher.install((Application) context, refWatcher);
  }
  return refWatcher;
}
  • 通过build方法创建RefWatcher对象
  • 正常来说,RefWatcher不是DISABLE,所以会走到if条件里面来。
  • ActivityRefWatcher.install关联RefWatcher和Activity的生命周期

接下来,我们进到ActivityWatcher.install方法,看看到底是怎么关联两者的。

1.2、ActivityRefWatcher.install

public static void install(Application application, RefWatcher refWatcher) {
  new ActivityRefWatcher(application, refWatcher).watchActivities();
}


public void watchActivities() {
  stopWatchingActivities();
  application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}


private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
    new Application.ActivityLifecycleCallbacks() {
      ......
      @Override public void onActivityDestroyed(Activity activity) {
        ActivityRefWatcher.this.onActivityDestroyed(activity);
      }
    };
  • 静态方法install创建了ActivityRefWatcher对象,该对象实现了生命周期回调lifecycleCallbacks。
  • 调用ActivityRefWatcher的watchActivities方法,将lifecycleCallbacks和RefWatcher关联。
  • 具体为lifecycleCallbacks.onActivityDestroyed回调方法会调用onActivityDestroyed方法,这个方法最终调用了RefWatcher.watch(activity)。

1.3、总结:

至此,LeakCanary的初始化工作已经全部结束。install方法的两个重要工作是创建RefWatcher,关联RefWacher和生命周期。

2、Refwatcher.watch

当Activity执行finish时,回调onDestory同时会触发ActivityRefWatcher的生命周期回调lifecycleCallbacks。根据1.2中的分析,此时会调用RefWatcher.watch。

public void watch(Object watchedReference) {
  watch(watchedReference, "");
}


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);
}
  • UUID.randomUUID.toStirng()生成随机的字符串并赋值给key,这个key代表了一个Activity对象。
  • 通过上面的key,引用队列queue,watchedReference(待内存泄漏分析的activity)传入KeyWeakReference得到一个自定义的弱引用对象。
  • watch传入了activity对象。通过activity,引用队列queue, key,得到了一个KeyedWeakReference 类型的弱引用对象reference。
  • 调用ensureGone检查activity是否发生内存泄漏。

3、Refwatcher.ensureGoneAsync

private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
  watchExecutor.execute(new Retryable() {
    @Override public Retryable.Result run() {
      return ensureGone(reference, watchStartNanoTime);
    }
  });
}
  • ensureGoneAsync异步调用ensureGone,执行内存泄漏分析

4、Refwatcher.ensureGone

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();
  removeWeaklyReachableReferences();
  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);
    heapdumpListener.analyze(
        new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
            gcDurationMs, heapDumpDurationMs));
  }
  return DONE;
}
  • removeWeaklyReachableReferences方法遍历引用队列,得到每一个弱引用对象,调用retainedKey.remove方法,清除列中中的key对象。前面我们讲到,retainedKey中每一个元素间接表示了一个需要被检查泄漏的对象。遍历和清除之后,剩下的元素,有可能存在内存泄漏。removeWeaklyReachableReferences的相关代码如下:
  • gone方法检查当前对象是否被GC。如果对象被GC,他的弱引用对象将会被放到引用队列。那么在removeWeaklyReachebleReference方法对将其弱引用的key从retainedKeys中清除。
  • 如果对象没有被GC,调用gcTrigger.runGc()方法,再执行一次GC,防止误报。GC之后再次执行removeWeaklyReachableReferences,和gone方法,检查对象时候回收。
  • 如果对象没有被回收,我们认定对象已经出现内存泄漏的情况。

4.1 、RefWatcher.removeWeaklyReachableReferences

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);
  }
}
  • 如果对象被垃圾回收器回收,其弱引用就会被加入到引用队列queue中。此时通过遍历引用队列,将KetWeakReference的key从集合中删除。

4.2、RefWatcher.gone

private boolean gone(KeyedWeakReference reference) {
  return !retainedKeys.contains(reference.key);
}
  • 用来判断是否存在内存泄漏的可能。
  • 如果没有内存泄漏,4.1的removeWeaklyReacheableReference方法就会将key值从集合中删除。换言之:如果当前集合包含了key,就可能存在内存泄漏。

4.3、HeapDumper.dumpHeap

@Override
public File dumpHeap() {
  File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();


  if (heapDumpFile == RETRY_LATER) {
    return RETRY_LATER;
  }


  FutureResult waitingForToast = new FutureResult<>();
  showToast(waitingForToast);


  if (!waitingForToast.wait(5, SECONDS)) {
    CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
    return RETRY_LATER;
  }


  Toast toast = waitingForToast.get();
  try {
    Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
    cancelToast(toast);
    return heapDumpFile;
  } catch (Exception e) {
    CanaryLog.d(e, "Could not dump heap"); 
    return RETRY_LATER;
  }
}
  • HeapDumper只是一个接口,他的实现在AndroidHeapDumper。
  • 19行,调用Debug.dumpHprofData方法,获取hprof文件。

4.4、ServiceHeapDumpListener.analyze

@Override public void analyze(HeapDump heapDump) {
  checkNotNull(heapDump, "heapDump");
  HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
  • analyze方法传入的是HeapDump对象。HeapAnalyzerService是IntentService类型,runAnalysis实现了异步调用,会走到HeapAnalyzerService.onHanderIntent方法中去。

4.5、HeapAnalyzerService.onHandlerIntent

@Override 
protected void onHandleIntent(Intent intent) {
  if (intent == null) {
    return;
  }
  String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
  HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);


  HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);


  AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
  AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
  • 在子线程中执行内存泄漏分析
  • checkForLeak检查内存泄漏并返回AnalysisResult分析结果

4.5.1、HeapAnalyzer.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);
    HprofParser parser = new HprofParser(buffer);
    Snapshot snapshot = parser.parse();
    deduplicateGcRoots(snapshot);


    Instance leakingRef = findLeakingReference(referenceKey, snapshot);


    if (leakingRef == null) {
      return noLeak(since(analysisStartNanoTime));
    }


    return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
  } catch (Throwable e) {
    return failure(e, since(analysisStartNanoTime));
  }
}
  • checkForLeak方法用来检查内存泄漏并返回检查结果AnalysisResult。第10行到12行是解析hprof文件,得到Snapshot。通过snapshot我们可以进一步分析内存泄漏。
  • findLeakingReference根据refrenceKey查找snapshot中的发生内存泄漏的KeyedWeakReference对象(前面提到的自定义弱引用对象)。
  • 如果leakingRef为空,说明没有内存泄漏
  • leakRef不为空,说明发生了内存泄漏,此时需要对内存泄漏进行分析。通过findLeakTrace调用查找引用链。
  • 4.5.1.1、HeapAnalyzerService.findLeakingReference
private Instance findLeakingReference(String key, Snapshot snapshot) {
  ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
  List keysFound = new ArrayList<>();
  for (Instance instance : refClass.getInstancesList()) {
    List values = classInstanceValues(instance);
    String keyCandidate = asString(fieldValue(values, "key"));
    if (keyCandidate.equals(key)) {
      return fieldValue(values, "referent");
    }
    keysFound.add(keyCandidate);
  }
  throw new IllegalStateException(
      "Could not find weak reference with key " + key + " in " + keysFound);
}
  • 通过SnapShot.findClass查找所有的KeyedWeakReference对象。遍历instance,找到Key为key的KeyedWeakReference。并返回,如果没有找到就抛出异常
  • 4.5.2.2、HeapAnalyzer.findLeakTrace
private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
    Instance leakingRef) {


  ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
  ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);


  if (result.leakingNode == null) {
    return noLeak(since(analysisStartNanoTime));
  }


  LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
  String className = leakingRef.getClassObj().getClassName();
  snapshot.computeDominators();
  Instance leakingInstance = result.leakingNode.instance;
  long retainedSize = leakingInstance.getTotalRetainedSize();
  
  if (SDK_INT <= N_MR1) {
    retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
  }


  return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
      since(analysisStartNanoTime));
}
  • 得到内存泄漏的instance之后,checkForLeak方法调用了findLeakTrace进行内存分析,并返回AnalysisResult。
  • buildLeakTrace方法得到内存泄漏的引用链
  • leakingInstance.getTotalRetainedSize()得到引用链长度
  • leakDetected返回AnalysisResult

4.5.2、AbstractAnalysisResultService.sendResultToListener

public static void sendResultToListener(Context context, String listenerServiceClassName,
    HeapDump heapDump, AnalysisResult result) {
  Class listenerServiceClass;
  try {
    listenerServiceClass = Class.forName(listenerServiceClassName);
  } catch (ClassNotFoundException e) {
    throw new RuntimeException(e);
  }
  Intent intent = new Intent(context, listenerServiceClass);
  intent.putExtra(HEAP_DUMP_EXTRA, heapDump);
  intent.putExtra(RESULT_EXTRA, result);
  context.startService(intent);
}


public AbstractAnalysisResultService() {
  super(AbstractAnalysisResultService.class.getName());
}
@Override protected final void onHandleIntent(Intent intent) {
  HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
  AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
  try {
    onHeapAnalyzed(heapDump, result);
  } finally {
    //noinspection ResultOfMethodCallIgnored
    heapDump.heapDumpFile.delete();
  }
}
  • AbstractAnalysisResultService是IntentService,最终会回调onHandleIntent。
  • onHandleIntent调用onHeapAnalyzed方法,这个方法是在DisplayLeakService类中实现的。

4.5.3、DisplayLeakService.onHeapAnalyzed

@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
  String leakInfo = leakInfo(this, heapDump, result, true);
  CanaryLog.d("%s", leakInfo);


  boolean resultSaved = false;
  boolean shouldSaveResult = result.leakFound || result.failure != null;
  if (shouldSaveResult) {
    heapDump = renameHeapdump(heapDump);
    resultSaved = saveResult(heapDump, result);
  }


  PendingIntent pendingIntent;
  String contentTitle;
  String contentText;


  if (!shouldSaveResult) {
    contentTitle = getString(R.string.leak_canary_no_leak_title);
    contentText = getString(R.string.leak_canary_no_leak_text);
    pendingIntent = null;
  } else if (resultSaved) {
    pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);


    if (result.failure == null) {
      String size = formatShortFileSize(this, result.retainedHeapSize);
      String className = classSimpleName(result.className);
      if (result.excludedLeak) {
        contentTitle = getString(R.string.leak_canary_leak_excluded, className, size);
      } else {
        contentTitle = getString(R.string.leak_canary_class_has_leaked, className, size);
      }
    } else {
      contentTitle = getString(R.string.leak_canary_analysis_failed);
    }
    contentText = getString(R.string.leak_canary_notification_message);
  } else {
    contentTitle = getString(R.string.leak_canary_could_not_save_title);
    contentText = getString(R.string.leak_canary_could_not_save_text);
    pendingIntent = null;
  }
  // New notification id every second.
  int notificationId = (int) (SystemClock.uptimeMillis() / 1000);
  showNotification(this, contentTitle, contentText, pendingIntent, notificationId);
  afterDefaultHandling(heapDump, result, leakInfo);
}
  • onHeapAnalyzed将内存泄漏的信息以前台通知发送

至此,LeakCanary的内存分析检测流程全部分析完

你可能感兴趣的:(Android,第三方源码分析,android,内存泄漏)