从LeakCanary探究线上内存泄漏检测方案

文章目录

  • 生成Dump文件
  • 解析Dump文件
  • 查找引用链
  • Listener组件
  • 泄漏引用链的显示
  • 总结
  • 拓展

19年年末总结一篇《LeakCanary原理从0到1》,当时还比较满意,以为自己就比较了解这个框架了,Too young, Too Simple。

周五群里一个小伙伴问:“线上做内存泄漏检测大家有什么思路吗?”。

内存泄漏检测首先想到的是 LeakCanary,可以看看能从LeakCanary上找到一些思路吗?

本文并不是从0开始解释 LeakCanary 的工作原理,所以为了阅读体验更佳,还不太了解 LeakCanary 是怎样判定对象内存泄漏的读者,可以先从《LeakCanary原理从0到1》开始阅读。

本文将从内存泄漏后 LeakCanary 的后续工作开始讲起,分析 LeakCanary 是怎么找到泄漏对象的强引用链的,分析 LeakCanary 不能直接用于线上内存检测的原因,并尝试找出线上检测内存泄漏的一些思路。

生成Dump文件

在判定有内存泄漏后,「LeakCanary」调用将系统提供的Debug.dumpHprofData(File file)函数,改函数将生成一个虚拟机的内存快照,文件格式为 .hprof,这个dump文件大小通常有10+M。(本文中所说的dump文件都是指内存快照的.hprof文件)

//:RefWatcher.java 
    Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime)
      ...
      //内部调用Debug.dumpHprofData(File file)函数
      File heapDumpFile = heapDumper.dumpHeap(file);

      HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile)
          .referenceKey(reference.key)
          .build();

      heapdumpListener.analyze(heapDump);
      ....
      return DONE;
  }

生成dump文件后,LeakCanary 将被泄露对象的 referenceKeydump 文件 对象封装在 HeapDump 对象中,然后交给ServiceHeapDumpListener处理,在ServiceHeapDumpListener中创建 leakcanary 进程并启动服务 HeapAnalyzerService

解析Dump文件

dump 文件的解析工作是在HeapAnalyzerService中完成的,主要逻辑入下:

//HeapAnalyzerService.java

    //创建一个分析器
    HeapAnalyzer heapAnalyzer =
        new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
    //使用分析器分析dump文件,得到分析结果
    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
        heapDump.computeRetainedHeapSize);
    //将分析结果交由listener组件处理
    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);

下面继续跟踪分析器逻辑,主要查看 HeapAnalyzercheckForLeak 方法:

//: HeapAnalyer.java

  public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey,
      boolean computeRetainedSize) {
   
      
      //读取dump文件,解析文件内容并生成一个Snapshot对象
      HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
      HprofParser parser = new HprofParser(buffer);
      Snapshot snapshot = parser.parse();
      
      //消除重复的GcRoots对象
      deduplicateGcRoots(snapshot);
      
      //通过referenceKey 在Snapshot对象中找到泄漏对象
      Instance leakingRef = findLeakingReference(referenceKey, snapshot);
      
      //找到泄漏路径
      return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
  }

checkForLeak方法中:

  1. 首先对dump文件的二进制数据进行解析,然后将文件内容信息存放在 Snapshot 对象当中,这种就可以从Snapshot中获得JVM的内存信息。(关于dump文件格式,有兴趣的可以点击这里,同时也可去看 square 的 com.squareup.haha:haha+,LeakCanary 使用的就是这个 dump 解析库)。
  2. 然后在 Snapshot 中类名为 KeyedWeakReferencereferenceKey 所对应的泄漏对象 Instence
  3. 最后在 Snapshot 中寻找泄漏对象 Instence 的泄漏强引用链

查找引用链

泄漏对象的引用链式如何被找到的呢?下面继续分析 findLeakTrace 方法:

//: HeapAnalyer.java

  private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
      Instance leakingRef, boolean computeRetainedSize) {
   
    

你可能感兴趣的:(Android)