Android LeakCanary源码解析

LeakCanary原理:watch一个即将要销毁的对象
1.在Activity 调用OnDestroy()之后将它放在一个WeakReference
2.将这个WeakReference关联到一个ReferenceQueue
3.查看ReferenceQueue是否存在Activity的引用
4.如果该Activit泄漏了,Dump出heap信息,然后再去分析泄漏路径

要了解内存泄漏首先要了解内存
内存分为:
1.栈(stack):存放基本类型和对象的引用
2.堆(heap):存放new出来,创建出来的对象或者数组,在堆中的内存是由Java虚拟机的垃圾回收器来管理的,JVM只有一个堆区,同时这个堆区会被所有线程所共享
3.方法区(method):方法区也可以称为静态区,被所有的线程共享的,方法区中还包含所有的class对象和静态变量

为什么会产生内存泄漏
1.对象不再需要使用,在对象需要被回收的时候,有其它正在使用的对象在引用它,导致这个本该被回收的对象不能被回收,本该被回收的对象还在堆内存中,就产生了内存泄漏
2.有些对象只有有限的生命周期,当这些生命周期很短的对象任务完成之后,就应该垃圾回收器回收,如果在这个对象的生命周期快被结束时,还被一系列的引用,就会导致内存泄漏

内存泄漏会导致什么问题
随着泄漏的不断累积,可能会消耗完整个内存,导致OOM

说到了引用,就说下引用类型
1.强引用(StrongReference):正常使用的对象就是强引用,强引用垃圾回收器是不会去回收强引用的对象,当内存不足,虚拟机宁愿抛出OOM的错误
2.软引用(SoftReference):内存空间足够的话,垃圾回收器不会去回收软引用的对象;但是内存空间不够的情况下,就回去回收软引用引用的对象的内存,只要垃圾回器没有回收这个软引用的对象,这个对象还是可以被使用的
3.弱引用(WeakReference):当垃圾回收器,扫描内存的时候,这时候不去区分内存够不够,会直接回收弱引用的对象
4.虚引用:形同虚设,不会决定一个对象的任何生命周期,垃圾回收器在任何情况下都可以回收

ReferenceQueue
软引用或者弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个引用加入到与之关联的引用队列中

看下LeakCanary.install方法

public static @NonNull RefWatcher install(@NonNull Application application) {//RefWatcher类是用于启动ActivityRefWatcher,然后通过ActivityRefWatcher去监视Activity的回收情况
    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
  }

看下buildAndInstall()方法

  public RefWatcher buildAndInstall() {
    RefWatcher refWatcher = build();//创建RefWatcher 对象
    if (refWatcher != DISABLED) {
      LeakCanary.enableDisplayLeakActivity(context);//LeakCanary就是使用的时候,弹框,告诉我们哪里有内存泄漏,enableDisplayLeakActivity开启Activity
      ActivityRefWatcher.install((Application) context, refWatcher);
    }
    return refWatcher;
  }

看下 ActivityRefWatcher.install(context, refWatcher)方法

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

```java
 public void watchActivities() {
    stopWatchingActivities();
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks);//重新注册activity的生命周期的Callback
  }

看下stopWatchingActivities()方法

public void stopWatchingActivities() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);//反注册以前的Activity生命周期的Calback,也就是保证我们以前所做的内存泄漏的监听工作要删除
  }
看下activityRefWatcher.lifecycleCallbacks源码

```java
  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new ActivityLifecycleCallbacksAdapter() {
        @Override public void onActivityDestroyed(Activity activity) {
          refWatcher.watch(activity);
        }
      };

看下ActivityLifecycleCallbacks

  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);//将Activity的生命周期关联到ActivityRefWatcher类当中
        }
      };

看下onActivityDestroyed(activity)方法

void onActivityDestroyed(Activity activity) {
    refWatcher.watch(activity);
  }

看下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;//用于处理GC的,判断内存泄漏之前,给泄漏对象一次机会,先调用一次GC,如果不GC就会把Dumper文件显示出来
  private final HeapDumper heapDumper;//Dumper出内存泄漏的堆文件
  private final Set<String> retainedKeys;//持有那些待检测的,以及那些产生内存泄漏的引用的key
  private final ReferenceQueue<Object> queue;//引用队列,主要是判断弱引用所持有的对象,是否已经被执行了GC垃圾回收
  private final HeapDump.Listener heapdumpListener;//主要是用于分析一些产生Heap文件的回调
  private final ExcludedRefs excludedRefs;//排除一些系统的bug引起的内存泄漏
                                      .
                                      .
                                      .
  }

看下 refWatcher.watch(activity)方法

 public void watch(Object watchedReference) {
    watch(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();//对Refenerence添加一个唯一的key值引用
    retainedKeys.add(key);//将唯一的key添加到前面说过的set集合当中
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);//创建所需要的弱引用对象

    ensureGoneAsync(watchStartNanoTime, reference);//开启一个异步线程来分析,刚才所创建好的KeyedWeakReference
  }

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

看下ensureGone(reference, watchStartNanoTime)方法

Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {//用来确保Activity是否已经进入Gone状态,Gone表示Activity是否真的已经被回收了
    long gcStartNanoTime = System.nanoTime();
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);

    removeWeaklyReachableReferences();

    if (debuggerControl.isDebuggerAttached()) {//判断如果是出于处于调试状态的时候,就不会进行内存泄漏的分析
      return RETRY;
    }
    if (gone(reference)) {//如果当前对象已经可达,已经不会造成内存泄漏,这就是不属于内存泄漏的范围
      return DONE;
    }
    gcTrigger.runGc();//gcTrigger进行垃圾回收的对象,gcTrigger.runGc()如果当前对象没有改变它的可达状态,进行手动的垃圾回收
    removeWeaklyReachableReferences();//清除已经到达引用队列的弱引用,把已经回收的对象的Key,从Set对象中移除,剩下的Key保证都是未被回收的
    if (!gone(reference)) {//表示已经出现内存泄漏
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);//用于计算调用watch这个方法到我们调用了GC垃圾回收总共所产生的时间
      File heapDumpFile = heapDumper.dumpHeap();//Dumper出我们所需要的内存泄漏文件
      if (heapDumpFile == RETRY_LATER) {
        // Could not dump the heap.
        return RETRY;
      }
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
      heapdumpListener.analyze( //  heapdumpListener.analyze去真正分析内存泄漏,以及它的路径
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
              gcDurationMs, heapDumpDurationMs));
    }
    return DONE;
  }

总结下:
1.首先会创建一个ActivityRefWatcher,启动一个ActivityRefWatcher
2.通过ActivityLifecycleCallbacks把activity的OnDestory生命周期关联
3.最后在线程池中去开始分析内存泄漏

看下 heapdumpListener.analyze( new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs, gcDurationMs, heapDumpDurationMs));这个抽象方法的实现

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

看下 HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);方法

public final class HeapAnalyzerService extends IntentService {//是个IntentService,最后会到 onHandleIntent(Intent intent)方法

  private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
  private static final String HEAPDUMP_EXTRA = "heapdump_extra";

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

  public HeapAnalyzerService() {
    super(HeapAnalyzerService.class.getSimpleName());
  }

  @Override protected void onHandleIntent(Intent intent) {
    if (intent == null) {
      CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
      return;
    }
    String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);//获取runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> listenerServiceClass)方法传进来的参数
    HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);

    HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);//用于分析堆内存的,heapDump.excludedRefs排除系统Bug所引起的内存泄漏

    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);//分析内存使用的结果
    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);//回调进行结果的显示
  }
}

看下heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey)方法

  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);//去重的GCRoot,就是会对分析的结果,去除重复的内存泄漏

      Instance leakingRef = findLeakingReference(referenceKey, snapshot);//根据需要检测的类Key,来查询检测结果中是否我们需要的对象

      // False alarm, weak reference was cleared in between key check and heap dump.
      if (leakingRef == null) {//表示GC后,对象已经被清除了
        return noLeak(since(analysisStartNanoTime));
      }

      return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);//有泄漏,找出整个路径
    } catch (Throwable e) {
      return failure(e, since(analysisStartNanoTime));
    }
  }

总结 checkForLeak(最重要的方法)
1.把.hprof转化为Snapshot
2.优化GCRoots
3.找出泄漏的对象/找出泄漏对象的最短路径

看下findLeakingReference(referenceKey, snapshot)方法

private Instance findLeakingReference(String key, Snapshot snapshot) {
    ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());//创建Class对象,通过KeyedWeakReference弱引用就可以查找出内存泄漏的对象
    List<String> keysFound = new ArrayList<>();
    for (Instance instance : refClass.getInstancesList()) {
      List<ClassInstance.FieldValue> 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);
  }

总结findLeakingReference方法
1.在Snapshot 快照中找到第一个弱引用(因为对象没有被回收,所以第一个对象就代表内存泄漏)
2.遍历这个对象的所有势力
3.如果Key值和最开始定义封装的Key值相同,那么返回这个泄漏对象

看下findLeakTrace(analysisStartNanoTime, snapshot, leakingRef)方法

private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
      Instance leakingRef) {//根据泄漏对象的引用,找到的对象,找出最短的路径

    ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
    ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);//这两行是判断内存泄漏的关键,通过分析hprof文件,找到内存泄漏的点,判断的依据就是GCRoot,GCRoot表示不能被垃圾回收器GC回收的对象。这里我们关注两种类型,第一种静态的;第二中被其它线程所引用,并且其它线程正在运行,没有结束

    // False alarm, no strong reference path to GC Roots.
    if (result.leakingNode == null) {
      return noLeak(since(analysisStartNanoTime));
    }

    LeakTrace leakTrace = buildLeakTrace(result.leakingNode);//生成的内存泄漏的调用栈,如果发生内存泄漏,展示在你的屏幕上的就是这个LeakTrace

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

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

    Instance leakingInstance = result.leakingNode.instance;

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

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

总结findLeakTrace方法
1.解析Hprof文件,把和这个文件封装成Snopshot
2.根据弱引用和前面定义的Key值,确定泄漏的对象
3.找到最短的泄漏路径,作为结果反馈出来 (如果找不到,可能是误判或者被垃圾回收器回收了)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值