2.Android 3分钟手写国内首个Leakcanary内存泄漏 吊打jakewharton大神

<pre style="margin: 8px 0px;">LeakCananry介绍: </pre>

LeakCananry是开源大户Square的一款开源产品,用于检测程序中的内存泄露

目前为止最新的版本是2.3版本,相比于2.0之前的版本,2.0之后的版本在使用上简洁了很多,只需要在dependencies中加入LeakCanary的依赖即可。而且debugImplementation只在debug模式下有效,所以不用担心用户在正式环境下也会出现LeakCanary收集。

LeakCanray 2.0为啥不需要在application里调install?

LeakCanary1.x 是基于 Java,LeakCanary2.x 是基于 Kotlin ,背后原理是类似的;

****如何安装的,自己是一个cotentprovider

这个什么时候初始化,activityThread 的作用

初始化

  • 在 1.x 中需要在 Application#onCreate 中显式初始化;

  • 在 2.x 中不需要,是因为初始化放在 ContentProvider

  • ContentProvider 在创建 Application 之前加载,LeakCanary 在 ContentProvider 中初始化;

有什么影响:启动速度慢。性能开销很大。只有开发的时候才用到。****

新版感觉不好用,引用链看不懂

它不会立刻内存泄露,要等一会儿。

3. ****解决内存泄漏的工具LeakCanary:

leakcanary存放hprof在哪

调用heapDumper.dumpHeap()生成.hprof文件目录

****一句话总结:分析.hprof文件和MAT一样****

缺点

1.LeakCanary的问题:LeakCanary也有一定的不确定性,一般同一个地方反复泄漏5次,算是一个泄漏,同时不建议用在线上环境。

2.LeakCanary 基于 Shark 分析引擎分析,分析速度较慢,通常在 5 分钟以上才能分析完成,分析过程会影响进程内存占用。

3.LeakCanary查找出来的是可能性有问题的。

** 4.****leakcanry尽管使用了idlehandler与分进程,但是dumphprof依然会造成应用明显的卡顿(SuspendAll Thread)。而在三星等一些手机,系统会缓存最后一个Activity,所以在微信,我们采取了更严格的检测模式,即泄露三次确认以及经过5个新建的Activity,确保不是由于系统缓存的原因造成。**

**内存泄露判断与处理的流程如下图 ,各自运行的进程空间(主进程通过idlehandler,HAHA分析使用的是单独的进程)******:****

leakcanary 原理://www.greatytc.com/p/5ee6b471970e

比较新的分析:

https://blog.csdn.net/chennai1101/article/details/103799424

LeakCanary原理总结:

  1. 初始化:直接debugImplementation就能实现,他是在 ContentProvider里面做的初始化,当打包的时候,会合并各个清单文件。里面注册的 ContentProvider,ContentProvider 会在 Application的 attachBaseContext 之后, onCreate之前创建。在ContentProvider的出事时候,

2. 在Application里面注册监控所以activitity的生命周期,注册 Application.ActivityLifecycleCallbacks 监听Activity的生命周期,以及 fragmentManager.registerFragmentLifecycleCallbacks监听Fragment的生命周期

3.ondestroy 调用RefWatcher的watch(),比如监听 onActivityDestroyed方法,当监听到这个方法调用的时候,把Activity 全部放到观察数组中,并且用引用队列包裹这个activity,生成key(UUID),然后过5s,看看引用队列里面有没有这个key,如果有,证明回收了,然后把观察数组中的remove掉这个key,此时如果这个数组里面的count > 0 ,证明有可能是怀疑的泄漏,然后 调用 Runtime.getRuntime().gc(),之后再 看看 引用队列有没有这个数据,如果有,然后把观察数组中的remove掉这个key,之后再看观察数组中的count,如果小于5,只是提示一下。如果count 大于 5 (防止卡顿),就开始使用 shark (2.0之前是haha)分析堆栈信息。用可达到性分析,找到最短的链路,

4.**然后在后台线程检查引用是否被清除,如果没有,调用GC。 **

5.****如果引用还是未被清除,dump ,然后生成一个.hprof文件

6.开一个服务和线程去分析和解析这个文件

6.计算 到 GC roots 的最短强引用路径(在ui会显示),并确定是否是泄露。如果是的话,建立导致泄露的引用链。

原理

1.LeakCanary通过ApplicationContext统一注册监听的方式,通过application.registerActivityLifecycleCallbacks来绑定Activity生命周期的监听,从而监控所有Activity;

2.在Activity执行onDestroy时,开始检测当前页面是否存在内存泄漏,并分析结果。

3.KeyedWeakReference与ReferenceQueue联合使用,在弱引用关联的对象被回收后,会将引用添加到ReferenceQueue;清空后,可以根据是否继续含有该引用来判定是否被回收;判定回收,

4.手动GC, 再次判定回收,采用双重判定来确保当前引用是否被回收的状态正确性;如果两次都未回收,则确定为泄漏对象。

调用步骤 :

在ondestory();

1、 首先通过removeWeaklyReachablereference来移除已经被回收的Activity引用

2、 通过gone(reference)判断当前弱引用对应的Activity是否已经被回收,如果已经回收说明activity能够被GC,直接返回即可。

3、 如果Activity没有被回收,调用GcTigger.runGc方法运行GC,手动GC完成后在运行第1步,然后运行第2步判断Activity是否被回收了,如果这时候还没有被回收,那就说明Activity可能已经泄露。

4、 如果Activity泄露了,就抓取内存dump文件

5、 之后通过HeapAnalyzerService.runAnalysis进行分析内存文件分析 ,分析profier文件.得到引用链

我自己的总结:

1.contentprovider启动

2.注册生命周期,在activity和fragment结束的时候会进行检测

3.引用队列RefrenceQueue,5s有没有被清楚。手动调用GC,Runtime

4.找到有问题的引用,开始分析(可达分析),是否是真正的内存泄露

这个工具可以查看内存快照,就是hprofiy文件

手写Leakcanary工具的核心代码。

ReferenceQueue referenceQueue = new ReferenceQueue<Object>();
Object object = new Object();
WeakReference<Object> weakReference = new WeakReference<Object>(object, referenceQueue);

Log.d("MyReference", "weakReference" + weakReference);
object = null;

Runtime.getRuntime().gc();

try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}

//从队列中取弱引用,如果不为空,说明有回收了,手动让object=null;
//如果为空,说明没有什么可以回收的
WeakReference<Object> weakReference1 = (WeakReference) referenceQueue.poll();
Log.d("MyReference", "after weakReference1" + weakReference1);

Object object1 = null;

if (referenceQueue.poll() != null) {
object1 = referenceQueue.poll().get();
}

Log.d("MyReference", "after object1" + object1);

打印:

2021-03-14 12:29:15.410 29008-29008/com.example.myapplication.myapplication D/MyReference: after weakReference1java.lang.ref.WeakReference@6146678

2021-03-14 12:29:15.410 29008-29008/com.example.myapplication.myapplication D/MyReference: after object1null

不把object置空,不会回收

2021-03-14 12:33:46.753 29009-29009/com.example.myapplication.myapplication D/MyReference: after weakReference1null

2021-03-14 12:33:46.753 29009-29009/com.example.myapplication.myapplication D/MyReference: after object1null

结论: 引用队列获取弱引用,如果是弱引用存在说明回收了,如果当GC过后引用对象仍然不被加入ReferenceQueue中,不存在说明内存泄漏了。 存在说明没有泄漏

核心原理:引用队列可以配合软引用、弱引用及虚引用使用,引用的对象将要被JVM回收时,会将其加入到引用队列中。

监测机制利用了Java的WeakReference和ReferenceQueue,弱引用和引用队列,!

在构造时我们需要传入一个ReferenceQueue,这个ReferenceQueue是直接传入了WeakReference中

通过将Activity包装到WeakReference中,被WeakReference包装过的Activity对象如果被回收,该WeakReference引用会被放到ReferenceQueue中,通过监测ReferenceQueue里面的内容就能检查到Activity是否能够被回收

Android 引用队列

核心原理:弱引用存放对象,当gc回收时候,ReferenceQueue存放引用,就是weak

引用队列可以配合软引用、弱引用及幽灵引用使用,当引用的对象将要被JVM回收时,会将其加入到引用队列中,LeakCanary就是用的这个原理来检测Activity或者Fragment是否有泄漏

Android 查看对象回收ReferenceQueue和内存泄漏?

WeakReference referencequequ

 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;
  private final ExcludedRefs excludedRefs;
  • 说一说Android中如何查看一个对象的回收情况 ?(字节跳动)

[图片上传失败...(image-92b695-1625703347881)]

首先要了解Java 四种引用类型的场景和使用(强引用、软引用、弱引用、虛引用)

o 举个场景例子:SoftReference 对象是用来保存软引用的,但它同时也是一个Java 对象,所以当软引用对象被回

收之后,虽然这个SoftReference 对象的get 方法返回null,但SoftReference 对象本身并不是null,而此时这个SoftReference 对象已经不再具有存在的价值,需要一

个适当的清除机制,避免大量SoftReference 对象带来的内存泄露o 因此,Java 提供ReferenceQueue 来处理引用对象的回收

情况。当SoftReference 所引用的对象被GC 后,JVM 会先将softReference 对象添加到ReferenceQueue 这个队列

中。当我们调用ReferenceQueue 的poll()方法,如果这个队列中不是空队列,那么将返回并移除前面添加的那个Reference 对

垃圾回收机制:

Android虚拟机的垃圾回收采用的是根搜索算法。GC会从根节点(GC Roots)开始对heap进行遍历。到最后,部分没有直接或者间接引用到GC Roots的就是需要回收的垃圾,会被GC回收掉。但是当对象不再被应用程序使用,仍然被生命周期长的对象引用,垃圾回收器无法回收。

demo地址:https://github.com/pengcaihua123456/shennandadao

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容