你就能够轻巧利用LeakCanary检查测试内部存款和储蓄器泄漏了,可是要是有一名目大多对那些指标的引用

简述

在性质优化中,内部存款和储蓄器是二个只好聊的话题;可是内存泄漏,展现已经成为内部存储器优化的1个重量级的趋势。当前风行的内部存储器泄漏分析工具中,不得不提的就是LeakCanary框架;那是二个并入方便,
使用简便,配置一流轻便的框架,达成的机能却是极为庞大的。


什么是内部存款和储蓄器败露

一对目的具有有限的生命周期。当那些指标所要做的工作完了了,大家期望她们会被回收掉。不过假设有1多级对这些指标的引用,那么在我们意在这几个目的生命周期甘休的时候被撤消的时候,它是不会被回收的。它还会占领内部存款和储蓄器,那就招致了内部存款和储蓄器败露。持续累加,内存相当的慢被耗尽。

比如,当 Activity.onDestroy被调用之后,activity 以及它事关到的 view
和血脉相通的 bitmap 都应该被回收。不过,假使有3个后台线程持有这一个 activity
的引用,那么 activity
对应的内部存款和储蓄器就不可能被回收。那最后将会促成内部存款和储蓄器耗尽,然后因为 OOM 而 crash。

不骗你,真的,使用正是如此简单 ?!

一. 你要求加上到安排的唯有那个

dependencies {

debugCompile ‘com.squareup.leakcanary:leakcanary-android:1.3’

releaseCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.3’

}

②. 你势必必要伊始化一下,当然, 推荐在Application中

public class MyApplicationextends Application {

        @Override

        public void onCreate() {

                super.onCreate();

                LeakCanary.install(this);

                }

      }

叁.
怎么?你还在下一步?已经达成了!通过上述配置,你就足以轻松使用LeakCanary检查测试内部存款和储蓄器泄漏了

   
关于LeakCanary的详尽使用教程,提议去看:LeakCanary粤语使用表明


聊天截止,小编想你们来自然不是看笔者这一个废话的,那么未来跻身正题!本篇我们所想的,正是LeakCanary为何能够那样美妙,它是怎么检验内部存款和储蓄器泄漏的?上边大家解开谜题!


LeakCanary

LeakCanary
Square厂商付出的2个用于质量评定OOM(out
of memory的缩写)难题的开源库。你能够在 debug 包种轻便检查评定内部存款和储蓄器败露。
Github地址:https://github.com/square/leakcanary

《LeakCanary原理》  主旨类分析 

01    LeakCanary                                        源码解析

02    LeakCanary   
                                    SDK提供类

03    DisplayLeakActivity  
                        内部存款和储蓄器泄漏的查阅页面

04    HeapAnalyzerService                         内部存款和储蓄器堆分析服务,
为了保障App进度不会就此受影响变慢&内存溢出,运转于独立的进程

05    HeapAnalyzer  
                                  剖析由Ref沃特cher生成的堆转储音讯,
验证内部存款和储蓄器泄漏是还是不是真实存在

06    HeapDump  
                                       堆转储音信类,存款和储蓄堆转储的相干新闻

07    ServiceHeapDumpListener  
             2个监听,包蕴了启封分析的点子

08    RefWatcher  
                                        主干类, 翻译自官方:
检验不可达引用(恐怕地),当发现不足达引用时,它会接触
 
                                                                       HeapDumper(堆新闻转储)

09    ActivityRefWatcher 
                             Activity引用检查测试,
包蕴了Activity生命周期的监听施行与截至

通过以上列表,让大家对LeakCanary框架的关键类有个差不多的垂询,并基于以上列表,对那一个框架的光景功用有3个歪曲的猜度。


漫无目标的看源码,很轻巧迷失在开阔的Code
Sea中,无论是看源码,依然接手别人的连串,都是那般;由此,带着难点与目的性来看那些复杂的事物是很有需要的,也使得我们涉猎功用大大升高;想要掌握LeakCanary,大家最大的吸引是什么,小编列出来,看看是与您不约而同。

Question一:   
在Application中起头化之后,它是怎么检查实验全体的Activity页面包车型大巴 ?

Question二:    内部存款和储蓄器泄漏的论断条件是什么 ?
检测内部存款和储蓄器泄漏的体制原理是如何?

Question三:    检查测试出内部存款和储蓄器泄漏后,它又是怎么变化泄漏音讯的?
内部存款和储蓄器泄漏的出口轨迹是怎么获得的?

回溯一下那一个框架,其实大家想打听的建制不外乎三:

  1. 内存泄漏的检查评定机制

  2. 内部存款和储蓄器泄漏的推断机制

  3. 内部存款和储蓄器泄漏的轨迹生成机制

小编们会在源码分析最终,依次回答上述的多个难点,大概在翻阅源码在此以前,我们先要对内部存款和储蓄器泄漏做壹些基础概念与原理的掌握。


如何是内部存储器泄漏(MemoryLeak)?

世家对那几个概念应该不面生吧,当大家使用三个Bitmap,使用完了后,未有recycle回收;当大家应用Handler,
在Activity销毁时不曾拍卖;当大家利用Cursor,最终没有close并置空;以上那几个都会招致一定水准上的内部存款和储蓄器泄漏难点。那么,什么是内部存款和储蓄器泄漏?

内部存款和储蓄器泄漏(Memory
Leak)是指程序中己动态分配的堆内部存储器由于某种原因程序未释放或无法自由,产生系统内部存储器的荒废,导致程序运维速度放慢甚至系统崩溃等严重后果。

上述是百度完善的分解,总括下为:内部存款和储蓄器泄漏是不使用或用完的内部存款和储蓄器,因为某个原因无法回收,变成的壹种内部存款和储蓄器浪费;内部存款和储蓄器泄漏的实质是内部存款和储蓄器浪费。以私家通晓来解说,通俗一点就是

  1. GC回收的对象必须是近期从未有过其余引用的目标

2.当对象在接纳产生后(对咱们来说已经是污源对象了),
大家一向不自由该指标的引用,导致GC不能够回收该目的而一而再据有内部存款和储蓄器

三.垃圾对象还是占有内部存款和储蓄器,那块内部存款和储蓄器空间便浪费了

内部存款和储蓄器泄漏与内部存款和储蓄器溢出的区分是怎么着?

从名称来看,八个外泄,四个溢出,其实很好领悟。

内部存款和储蓄器泄漏
垃圾对象仍然占有内部存储器,如水阀的泄漏,水自然是属于基本的,
然则水龙头没关紧,那么泄漏到了水池;再来看内部存款和储蓄器,内部存款和储蓄器本来应             
     
 该被回收,可是依旧在内部存款和储蓄器堆中;总括一下就是内部存款和储蓄器存在于不应该存在的地点(没用的地点)

内存溢出
内部存储器占用达到最大值,当供给分配内部存款和储蓄器时,已经未有内存能够分配了,就是溢出;依然以水池为例,
水池的水壹旦满了,那么1旦继                     
续须要从水阀流水的话,水就会溢出。总括一下正是,内部存款和储蓄器的分红赶过最大阀值,导致了1种卓殊

清楚了两者的概念,那么双方有啥关联吧?

内部存款和储蓄器的溢出是内部存款和储蓄器分配达到了最大值,而内部存款和储蓄器泄漏是无用内部存款和储蓄器充斥了内部存款和储蓄器堆;因而内部存款和储蓄器泄漏是形成内存溢出的罪魁祸首之一,而且是不小的罪魁祸首;因为内部存款和储蓄器分配完后,哪怕占用再大,也会回收,而泄漏的内部存储器则不然;当清理掉无用内部存款和储蓄器后,内部存款和储蓄器溢出的阀值也会相应下落。


JVM怎么着判断多个指标是垃圾堆对象?

该难点也即垃圾对象寻找算法,JVM选择图论的可达遍历算法来决断1个对象是不是是垃圾对象,
纵然对象A是可达的,则以为该目的是被引述的,GC不会回收;即便指标A恐怕块B(多少个对象引用组成的指标块)是不可达的,那么该目的只怕块则判别是不可达的垃圾堆对象,GC会回收。


如上科学普及的多个小知识:一) 内部存款和储蓄器泄漏  贰) JVM寻找算法
是阅读LeakCanary源码的功底,有助于源码的知道与回忆。好了,上边来看一下LeakCanary的源码,看看LeakCanary是怎么工作的呢!

既然LeakCanary的初叶化是从install()先导的,那么从init开端看

追思一下着力类模块可见,内部存款和储蓄器分析模块是在单身进度中进行的,这么设计是为着保障内部存款和储蓄器分析进度不会对App进度形成伤心的熏陶,如使App进度变慢或促成out
of Memory难题等。由此

首先步: 判定APP进程与内部存储器分析进度是或不是属于同一进度;若是是,
则重回空的RefWatcher DISABLED;假使不是,往下走,看第三步

是还是不是与分析进度是同2个, isInAnalyzerProcess

与分析进程一致,重返1个空的DISABLED

其次步: enableDisplayLeakActivity 开启突显内部存款和储蓄器泄漏新闻的页面

调用了setEnabled,记住参数DisplayLeakActivity正是翻开泄漏的页面,enable传true,开启Activity组件

以此艺术的法力是设置四大组件开启或剥夺,从上图传的参数看,是张开了查看内部存款和储蓄器泄漏的页面

其三步:初始化二个ServiceHeapDumpListener,那是八个敞开分析的接口达成类,类中定义了analyze方法,用于开启二个DisplayLeakService服务,从名字就足以看看,那是贰个显示内部存款和储蓄器泄漏的支持劳动

看注释,那一个服务的效益是分析HeapDump,写入2个记下文件,并弹出一个Notification

第五步:初阶化多少个沃特cher, Ref沃特cher和ActivityRef沃特cher.
那七个Watcher的功能分别为分析内存泄漏与监听Activity生命周期

ActivityRef沃特cher监听Activity生命周期,在初阶化时开头监听Activity生命周期(watchActivities)

watchActivities中登记了具有Activity的生命周期统一监听;onActiityDestroy会在onDestroy时施行,施行watch,检查测试内部存款和储蓄器泄漏

怎么着使用

引进LeakCanary库 ,在品种的build.gradle文件加多:

   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'

gradle 庞大的可配置性,能够保障只在编译 debug
版本时才会检讨内存败露,而编写翻译 release
等版本的时候则会活动跳过检查,制止影响属性。
在自定义的Application中起头化 ,LeakCanary.install()会回来二个预订义的
Ref沃特cher,同时也会启用1个 ActivityRef沃特cher,用于机动监控调用
Activity.onDestroy()之后泄的activity。

1经只想检验Activity的内部存款和储蓄器走漏,只必要丰盛那一行代码 。

 private static RefWatcher refWatcher;
 @Override
    public void onCreate() {
        super.onCreate();
        //LeakCanary 就会自动侦测 activity 的内存泄露
        // 会返回一个预定义的 RefWatcher ,同时也会启用一个 ActivityRefWatcher,用于自动监控调用 Activity.onDestroy() 之后泄露的 activity。
        refWatcher = LeakCanary.install(this);
    }

假诺还想质量评定fragment

public class BaseFragment extends Fragment {
    //使用 RefWatcher 监控 Fragment:
    @Override
    public void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = MyApplication.getRefWatcher();
        refWatcher.watch(this);
    }
}

当您在测试debug版本进程中出现内部存储器败露时,LeakCanary将会自动呈现3个文告栏

图片 1

screenshot.png

由此布告里的消息,我们能够化解广大内部存款和储蓄器败露难点 。

备考:LeakCanary只援助4.0之上,原因是中间在watch
每一种Activity时调用了Application的registerActivityLifecycleCallback函数,那一个函数只在4.0上才支撑,但是在四.0之下也是能够用的,可以在Application旅长重回的Ref沃特cher存下来,然后在基类Activity的onDestroy函数中调用。

通过以上代码分析,大家得以汲取第3个难题的答案。LeakCanary通过ApplicationContext统1登记监听的方法,来监督全数的Activity生命周期,并在Activity的onDestroy时,施行Ref沃特cher的watch方法,该方法的效益就是检查测试本页面内是否留存内部存款和储蓄器泄漏难题。


上面大家连续来分析宗旨类Ref沃特cher中的源码,检测机制的中央逻辑便在Ref沃特cher中;相信阅读完那个类后,第3个难题的答案便维妙维肖了。

既是想弄明白Ref沃特cher做了怎么,那么先来看一下官方的演说

监听大概不可达的引用,当Ref沃特cher剖断1个引用大概不足达后,会触发HeapDumper(堆转储)

从上边图能够看来官方的讲解。
Ref沃特cher是二个引用检查测试类,它会监听大概晤面世泄漏(不可达)的目的引用,假使发现该引用可能是泄漏,那么会将它的新闻征集起来(HeapDumper).

从Ref沃特cher源码来看,主旨措施首要有五个: watch()
和 ensureGone()。如若大家想单独监听某块代码,如fragment或View等,大家要求手动去调用watch()来检查测试;因为上边讲过,默许的watch()仅施行于Activity的Destroy时。watch()是大家一贯调用的章程,ensureGone()则是实际如何处理了,下边大家来看一下 

watch 检查实验主旨措施

上海教室为watch()的源码, 大家先来看一下官方的讲授

监听提供的引用,检查该引用是或不是足以被回收。那一个艺术是非阻塞的,因为检验作用是在Executor中的异步线程试行的

从上述源码能够看到,watch里面只是实践了迟早的预备工作,如判空(checkNotNull),
为每种引用生成二个唯一的key,
初阶化KeyedWeakReference;关键代码依旧在watchExecutor中异步奉行。引用检查测试是在异步试行的,因而那些历程不会卡住线程。

检验中央代码 gone()判断WeakReference中是不是包罗当前援引

以上是检测的基本代码达成,从源码能够看出,检查测试的流水生产线:

1) 移除不可达引用,假设当前引述不存在了,则不继续施行

2) 手动触发GC操作,gcTrigger中封装了gc操作的代码 

三) 再度移除不可达引用,假若引用不存在了,则不继续施行

四) 如若五回判断都未有被回收,则开首分析这几个引用,最后生成HeapDump音信

小结一下规律:

一.
弱引用与ReferenceQueue联合利用,假若弱引用关联的靶子被回收,则会把这些弱引用到场到ReferenceQueue中;通过那么些规律,能够看看removeWeaklyReachableReferences()执行后,会对应除去KeyedWeakReference的数据。尽管这一个引用继续存在,那么就印证未有被回收。

2.
为了有限支撑最大保险的决断是或不是被回收,一共试行了两遍回收剖断,包罗3遍手动GC后的回收判别。四次都未曾被回收,相当大程度上证实了那么些指标的内部存款和储蓄器被败露了,但并不可能百分之百担保;因而LeakCanary是存在非常的小程度的抽样误差的。

上面的代码,总计下流程便是

看清是不是回收(KeyedWeakReference是不是存在该引用), Y -> 退出, N
-> 向下奉行

手动触发GC

判断是还是不是回收, Y -> 退出, N-> 向下进行

三回未被回收,则分析引用情形:

一) humpHeap :  这几个方式是生成1个文件,来保存内部存款和储蓄器分析音信 

二) analyze: 实践分析

经过上述的代码分析,第二个难点的答案已经浮出水面了吧!


接下去分析内部存款和储蓄器泄漏轨迹的变动~

末段的调用,是在Ref沃特cher中的ensureGone()中的最终,如图

浅析最后调用,在ensureGone()中

很鲜明,走的是heapdumpListener中的analyze方法,继续追踪heapdumpListener是在LeakCanary开头化的时候发轫化并传播Ref沃特cher的,如图

在install中早先化并传到Ref沃特cher

打开进去ServiceHeapDumpListener,看个中完结,如图

ServiceHeapDumpListener中的analyze

调用了HeapAnalyzerService,在单独的长河中进行解析,如图 

HeapAnalyzerService分析进程

HeapAnalyzerService中经过HeapAnalyzer来拓展实际的分析,查看HeapAnalyzer源码,如图

HeapAnalyzer

开始展览解析时,调用了openSnapshot方法,里面用到了SnapshotFactory

org.eclipse.mat

从上海体育场所能够见见,这些版本的LeakCanary选拔了MAT对内部存款和储蓄器音信举行辨析,并生成结果。在那之中在分析时,分为findLeakingReference与findLeakTrace来索求泄漏的引用与轨道,依据GCRoot起首按树形结构依次建议当前引述的轨道消息。


因此上述分析,最后得出的结果为:

1. Activity检验机制是什么?

答:
通过application.registerActivityLifecycleCallbacks来绑定Activity生命周期的监听,从而监察和控制全数Activity;
在Activity推行onDestroy时,伊始检查实验当前页面是还是不是存在内部存款和储蓄器泄漏,并分析结果。由此,假使想要在差异的地方都急需检验是不是存在内部存款和储蓄器泄漏,必要手动增多。

贰. 内部存款和储蓄器泄漏检查评定机制是什么样?

答:
KeyedWeakReference与ReferenceQueue联合利用,在弱引用关联的靶子被回收后,会将引用增加到ReferenceQueue;清空后,能够依照是不是再三再四含有该引用来判别是还是不是被回收;判别回收,
手动GC,
再度判断回收,选用重复决断来保证当前援引是或不是被回收的状态不错;固然两回都未回收,则显著为泄漏对象。

3. 内部存款和储蓄器泄漏轨迹的改变进程 ?

答: 该版本采纳eclipse.Mat来分析泄漏详细,从GCRoot起头逐年转移引用轨迹。

经过整篇小说分析,你还在困惑么?

干活原理

简短概述一下, 源码还未曾分析掌握 。

  1. RefWatcher.watch()创立2个
    KeyedWeakReference
    到要被监督的目的 (约等于弱引用)。
  2. 下一场在后台线程检查引用是或不是被免去,倘若未有,调用GC。
  3. 壹旦引用依然未被免除,把 heap 内部存款和储蓄器 dump 到 应用程式对应的文件系统中的3个 .hprof文件中。
  4. 在其余三个进度中的 HeapAnalyzerService有3个HeapAnalyzer使用HAHA
    解析这一个文件。得益于唯壹的 reference key, HeapAnalyzer找到
    KeyedWeakReference,定位内部存款和储蓄器败露。
  5. HeapAnalyzer计算 到 GC roots
    的最短强引用路线
    ,并规定是或不是是走漏。假如是的话,建立导致败露的引用链。
  6. 引用链传递到 APP 进度中的 DisplayLeakService,
    并以布告的样式显得出来。

源码层面轻便解析

RefWatch

Reft沃特cher是leakcancay检查评定内部存款和储蓄器走漏的发源点。使用办法为,在对象生命周期将在完工的时候,调用

    RefWatcher.watch(Object object)

为了达成检查测试内部存款和储蓄器走漏的指标,Ref沃特cher要求

  private final Executor watchExecutor;
  private final DebuggerControl debuggerControl;
  private final GcTrigger gcTrigger;
  private final HeapDumper heapDumper;
  private final Set<String> retainedKeys;
  private final ReferenceQueue<Object> queue;
  private final HeapDump.Listener heapdumpListener;
  • watchExecutor: 实践内部存款和储蓄器败露检查测试的executor
  • debuggerControl :用于查询是不是正在调节和测试中,调节和测试中不会施行内部存款和储蓄器败露检查实验
  • gcTrigger: 用于在认清内部存款和储蓄器走漏从前,再给叁回GC的火候
  • headDumper: 用于在发出内部存款和储蓄器败露室实行dump 内部存款和储蓄器heap
  • retainedKeys: 持有那多少个呆检验以及发生内部存款和储蓄器走漏的引用的key
  • queue : 用于判定弱引用所怀有的靶子是不是已被GC。
  • heapdumpListener: 用于分析前边产生的dump文件,找到内存败露的缘故
    接下去,大家来看望watch函数背后是怎么着使用那几个工具,生成内部存款和储蓄器败露分析报告的。

 /**
   * Watches the provided references and checks if it can be GCed. This method is non blocking,
   * the check is done on the {@link Executor} this {@link RefWatcher} has been constructed with.
   *
   * @param referenceName An logical identifier for the watched object.
   */
  public void watch(Object watchedReference, String referenceName) {
    checkNotNull(watchedReference, "watchedReference");
    checkNotNull(referenceName, "referenceName");
    // 如果处于debug模式,直接return
    if (debuggerControl.isDebuggerAttached()) {
      return;
    }
    //记住开始观测的时间
    final long watchStartNanoTime = System.nanoTime();
    //生成一个随机的key,并加入set中
    String key = UUID.randomUUID().toString();
    retainedKeys.add(key);
    //生成一个KeyedWeakReference
    final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue);
     //调用watchExecutor,执行内存泄露的检测
    watchExecutor.execute(new Runnable() {
      @Override public void run() {
           ensureGone(reference, watchStartNanoTime);
       }
    });
  }

之所以最后的中坚函数是在ensureGone这几个艺术里面。要明了其职业原理,就得从keyedWeakReference聊到

WeakReference与ReferenceQueue

从watch函数中,能够看来,每一回检验对象内部存款和储蓄器是不是泄漏时,大家都会变卦1个KeyedReferenceQueue,这几个类其实就是2个WeakReference,只可是其额外顺带了叁个key和三个name

/** @see {@link HeapDump#referenceKey}. */
final class KeyedWeakReference extends WeakReference<Object> {
  public final String key;
  public final String name;

  KeyedWeakReference(Object referent, String key, String name,
      ReferenceQueue<Object> referenceQueue) {
      super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
      this.key = checkNotNull(key, "key");
      this.name = checkNotNull(name, "name");
  }
}

在结构时大家需求传入三个ReferenceQueue,那些ReferenceQueue是一向传入了WeakReference中,关于那个类,有意思味的能够一向看Reference的源码。我们那边须求领会的是,每回WeakReference所指向的靶子被GC后,这么些弱引用都会被放入那一个与之相关联的ReferenceQueue队列中。

在reference类加载的时候,java虚拟机会成立一个最大优先级的后台线程,这几个线程的劳作规律正是连绵不断检验pending是还是不是为null,假若不为null,就将其放入ReferenceQueue中,pending不为null的状态便是,引用所针对的指标已被GC,变为不可达。

那正是说只要大家在协会弱引用的时候内定了ReferenceQueue,每当弱引用所指向的指标被内部存款和储蓄器回收的时候,大家就能够在queue中找到那个引用。假设大家目的在于二个对象被回收,那假诺在接下去的料想时间之后,大家发现它照旧未有出现在ReferenceQueue中,那就可以判明它的内部存款和储蓄器败露了。LeakCanary检验内存败露的中坚原理就在此处。

监测时机

什么样时候去检查测试能剖断内部存款和储蓄器走漏呢?这几个能够看Android沃特chExecutor的兑现

public final class AndroidWatchExecutor implements Executor {
  private final Handler backgroundHandler;

  public AndroidWatchExecutor() {
    mainHandler = new Handler(Looper.getMainLooper());
    HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
    handlerThread.start();
    backgroundHandler = new Handler(handlerThread.getLooper());
  }
  ....
  private void executeDelayedAfterIdleUnsafe(final Runnable runnable) {
    // This needs to be called from the main thread.
    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
      @Override public boolean queueIdle() {
         backgroundHandler.postDelayed(runnable, DELAY_MILLIS);
         return false;
      }
    });
  }
}

此间又来看1个比较少的用法,IdleHandler,IdleHandler的原理就是在messageQueue因为空闲等待音信时给使用者四个hook。那Android沃特chExecutor会在主线程空闲的时候,派发二个后台职责,这几个后台职务会在DELAY_MILLIS时间之后实践。LeakCanary设置的是伍秒。

二回承认保障内部存款和储蓄器走漏准确性

为了防止因为gc不立时带来的误判,leakcanay会实行三次确认进行担保。

void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    //计算从调用watch到进行检测的时间段
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    //根据queue移除已被GC的对象的弱引用
    removeWeaklyReachableReferences();
    //如果内存已被回收或者处于debug模式,直接返回
    if (gone(reference) || debuggerControl.isDebuggerAttached()) {
      return;
    }
    //如果内存依旧没被释放,则再给一次gc的机会
    gcTrigger.runGc();
    //再次移除
    removeWeaklyReachableReferences();
    if (!gone(reference)) {
      //走到这里,认为内存确实泄露了
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

      File heapDumpFile = heapDumper.dumpHeap();

      if (heapDumpFile == null) {
        // Could not dump the heap, abort.
        return;
      }
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
      heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs,
              heapDumpDurationMs));
    }
  }

Dump Heap

监测到内部存款和储蓄器败露后,首先做的便是dump出最近的heap,默认的AndroidHeapDumper调用的是

    Debug.dumpHprofData(filePath);

导出当前内部存款和储蓄器的hprof分析文件,壹般大家在DeviceMonitor中也足以dump出hprof文件,然后将其从dalvik格式转成标准jvm格式,然后使用MAT举办辨析。

那么LeakCanary是如何分析内部存款和储蓄器败露的呢?

HaHa

LeakCanary
分析内部存款和储蓄器败露用到了二个和Mat类似的工具叫做HaHa,使用HaHa的不二等秘书籍如下:

 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();

            Instance leakingRef = findLeakingReference(referenceKey, snapshot);

            // False alarm, weak reference was cleared in between key check and heap dump.
            if (leakingRef == null) {
                return noLeak(since(analysisStartNanoTime));
            }

            return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
        } catch (Throwable e) {
            return failure(e, since(analysisStartNanoTime));
        }
    }

回来的ActivityResult对象中蕴藏了指标到GC
root的最短路线。LeakCanary在dump出hprof文件后,会运维2个IntentService举办辨析:HeapAnalyzerService在条分缕析出结果之后会运转DisplayLeakService用来倡导Notification
以及将结果记录下来写在文书之中。今后每便运营LeakAnalyzerActivity就从文件里读取历史结果。

参考文书档案

LeakCanary
华语使用表达

LeakCanary
内部存款和储蓄器走漏监测原理钻探
:
结合源码分析leakcanary检查内部存款和储蓄器败露的历程。

相关文章