Bootstrap

LeakCanary原理分析

LeakCanary是一个检测内存泄漏的工具,使用非常简单。主要用来检测Activity和Fragment内存泄漏,如果发生内存泄漏,直接在用UI显示哪里发生了泄漏并展示对象引用链。

LeakCanary地址:https://github.com/square/leakcanary

LeakCanary的使用

在gradle文件中加入依赖

debugImplementation   'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
 // 如果使用了 support fragment,需要依赖
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'

在Application中加入

public class MyApplication extends Application {
   
    private RefWatcher refWatcher = null;
    @Override
    public void onCreate() {
   
        super.onCreate();
        if (!LeakCanary.isInAnalyzerProcess(this)) {
    // 是否在主进程
            refWatcher = LeakCanary.install(this);
        }
    }
}

如果想监视其他的Object是否内存泄漏了

refWatcher.watch(Object object)
原理

在LeakCanary 的 wiki 就简述了基本的原理:

  1. RefWatcher.watch() 为监视的对象创建一个 KeyedWeakReference;
  2. 然后,后台线程会检查引用是否被清除回收了,如果没有就触发GC;
  3. 如果引用还没被清除,则堆内存dump到文件系统,成一个.hprof文件
  4. HeapAnalyzerService运行在另外一个进程,其中内部的HeapAnalyzer 使用HAHA库解析heap dump
  5. 根据reference key,HeapAnalyzer 找到对应的 KeyedWeakReference,定位内存泄露
  6. HeapAnalyzer 可以找出 GC roots 的最短强引用路径,并确定是否是泄露。如果泄漏,建立导致泄露的引用链。
  7. 将泄漏结果(引用链)传给App进程DisplayLeakService,通知展示

我们主要看的就是LeakCanary.install(this)到底发生了什么?

在解析流程之前先看下涉及到的几个重要的类:

在这里插入图片描述

  1. RefWatcher: 核心类,负责管理和提供入口watch(),由AndroidRefWatcherBuilder创建RefWatcher,建造者模式

  2. WatchExecutor: 负责控制执行检测内存泄漏任务

  3. DebuggerControl:判断是否处于debug

  4. GcTrigger:负责触发一次GC

  5. HeapDump:表示指定时刻的堆栈的快照,AndroidHeapDump为子类

  6. HeapDump.Builder: 负责创建HeapDump

  7. HeapDump.Listener:监听器,当发生内存泄漏的时候,会收到消息,需要触发分析AndroidHeapDump任务

  8. ServiceHeapDumpListener:HeapDump.Listener的实现类,当触发分析任务,调用HeapAnalyzerService执行分析任务

  9. HeapAnalyzerService:是一个Android中四大组件之一的Service,运行在独立的进程,负责执行分析任务和UI通知

  10. HeapAnalyzer:在HeapAnalyzerService内部中,是对DumpHeap分析内存泄漏和找出引用链的工具

  11. retainKeys: 是一个Set,保存着当前还没被回收的Reference的key

  12. ReferenceQueue:引用队列,WeakReference可以关联引用队列,当reference被回收时,会被加入到ReferenceQueue,这样我们就可以判断哪些对象没有被回收了

  13. DisplayLeakService:记录泄漏日志和展示通知的Service

其实,leakCanary的基本原理就是利用ReferenceQueue,在Activity销毁的时候判断对象有没有被加入ReferenceQueue,若没有则说明Activity还在存活,可能存在泄漏。

GC Root的种类

java 使用引用链法来判断一个引用是否该被回收,而出发点就是GC Root

  1. 虚拟机栈的对象引用
  2. 本地方法栈的对象引用
  3. 方法区的常量引用
  4. 方法区的静态对象引用
源码分析

isInAnalyzerProcess()方法是用来判断当前的进程是否是在分析进程,因为多进程中,每个进程都会创建Application,而我们需要在非LeakCanary的进程中检测。

// LeakCanary
public static boolean isInAnalyzerProcess(@NonNull Context context) {
   
  Boolean isInAnalyzerProcess = LeakCanaryInternals.isInAnalyzerProcess;
  // This only needs to be computed once per process.
  if (isInAnalyzerProcess == null) {
   
    isInAnalyzerProcess = isInServiceProcess(context, HeapAnalyzerService.class);
    LeakCanaryInternals.isInAnalyzerProcess = isInAnalyzerProcess;
  }
  return isInAnalyzerProcess;
}

install方法是LeakCanary的总入口

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

这里使用了Build建造者模式,利用AndroidRefWatcherBuilder创建RefWatcher

我们分成几个部分:

  1. refWatch(application) 创建AndroidRefWatcherBuilder
  2. listenerServiceClass(DisplayLeakService.class) 设置监听内存泄漏和分析结果的 Service
  3. excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) 设置忽略的内存泄漏的错误
  4. buildAndInstall() 初始化AndroidRefWatcher
1. refWatch(application)
// LeakCanary
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
   
  return new AndroidRefWatcherBuilder(context);
}

这里只是创建了AndroidRefWatcherBuilder,最后使用Build模式创建AndroidRefWatcher

2.listenerServiceClass(DisplayLeakService.class)
// AndroidRefWatcherBuilder 
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
    @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
   
  enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass); 
  return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}

创建了heapDumpListener负责heapDump的分析和处理

isAssignableFrom() 是object方法,
例如 a.isAssignableFrom(b) 判断a是不是b的父类或接口

如果listenerServiceClass继承或者是DisplayLeakService,则enableDisplayLeakActiviy为true,表示显示LeakCanary界面

// ServiceHeapDumpListener 
public final class ServiceHeapDumpListener implements HeapDump.Listener {
   

  private final Context context;
  private final Class<? extends AbstractAnalysisResultService> listenerServiceClass;

  public ServiceHeapDumpListener(@NonNull final Context context,
      @NonNull final Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
   
    this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
    this.context = checkNotNull(context, "context").getApplicationContext();
  }

  @Override public void analyze(@NonNull HeapDump heapDump) {
   
    checkNotNull(heapDump, "heapDump");
    HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
  }
}

这里简单看一下ServiceHeapDumpListener ,主要方法在analyze(),实际上也只是通知了HeapAnalyzerService.runAnalysis()去分析heapDump;

HeapAnalyzerService是运行在独立进程的Service。它是一个IntentService,执行完一次任务就会终止。

3. excludedRefs(AndroidExcludedRefs.createAppDefaults().build())

AndroidExcludedRefs.java类中维护着一系列的特定的内存泄漏类型,在执行内存泄漏和显示的时候也会忽略这些类

4. buildAndInstall()

                
      
;