内存分析工具LeakCanary是如何工作的
一旦LeakCanary被安装,它自动检测和报告内存泄漏,分4步:
- 检测保留下来的对象;
- 导出堆信息;
- 分析堆信息;
- 对内存泄漏进行分类;
目录
1.检测保留下来的对象
2.导出堆文件
3.分析堆文件
4.对内存泄漏进行分类
5.其他
1.检测保留下来的对象
LeakCanary通过Hook(劫持)Android生命周期去自动检测内存泄漏问题,当Activity和Fragment被销毁并且执行垃圾回收的时候;这些被销毁的对象被传递给ObjectWatcher(持有这些销毁的对象的弱引用);LeakCanary自动检测如下对象的内存泄漏:
a.已销毁的Activity实例;
b.已销毁的Fragment实例;
c.已销毁的片段View实例;
d.已经清除的ViewModel实例;
可以检测任何不在需要的对象,例如一个被移除的View或者一个销毁的Presenter:
AppWatcher.INSTANCE.getObjectWatcher().watch(textView2, "View was detached");
如果持有弱引用销毁对象的ObjectWatcher在等候5秒并且运行垃圾回收不能被清除,被观察的销毁对象可能被保留,存在潜在的内存泄漏问题;
LeakCanary输入日志在Logcat控制台下:
D LeakCanary: Watching instance of com.example.leakcanary.MainActivity(Activity received Activity#onDestroy() callback) ... 5 seconds later ...D LeakCanary: Scheduling check for retained objects because found new objectretained
LeakCanary一直等候被保留未销毁的对象数量达到阀值(5)再导出heap堆hprof文件,并在通知栏显示最新未销毁对象的数量;
通知提示有4个未销毁的对象被保留,点击通知可以导出heap堆文件;
D LeakCanary: Rescheduling check for retained objects in 2000ms because foundonly 4 retained objects (< 5 while app visible)
注意:
默认阈值为应用程序可见时5个保留对象,应用程序不可见时1个保留对象。如果您看到retained objects通知,然后将应用程序置于后台(例如按Home按钮),那么阈值将从5更改为1,LeakCanary将在5秒内导出堆文件。点击通知会强制LeakCanary立即导出堆文件。
2.导出堆文件
当未销毁对象被保留达到阀值,LeakCanary导出Java的堆信息存储到hprof文件;导出heap堆文件会短暂冻结APP,在导出堆文件时会有如下通知:
默认存储堆文件在app文件夹下的leakcanary目录下,如果设置android.permission.WRITE_EXTERNAL_STORAGE权限并授权此权限,则堆文件存储在SD卡的Download/leakcanary-com.example目录下,com.example是app的包名;
3.分析堆文件
Shark: Smart Heap Analysis Reports for Kotlin;
Shark是为LeakCanary 2提供功能强大的堆分析器。它是一个Kotlin独立堆分析库,以低内存占用率高速运行。
Shark被支持如下功能:
a.Shark Hprof:读取和写入Hprof文件中的记录。
b.Shark Graph:导航堆对象图。
c.Shark:生成堆分析报告。
d.Shark Android:Android启发式生成定制的堆分析报告。
e.Shark CLI:分析安装在连接到桌面的Android设备上的可调试应用程序堆。输出与LeakCanary的输出类似,只是您不必将LeakCanary依赖项添加到应用程序中。
LeakCanary:建在上面。它会自动监视被销毁的Activity和Fragment,触发堆存储,运行Shark Android,然后显示结果。
06-27 15:19:38.515 9186-9224/fan.fragmentdemo D/LeakCanary: Removing 1 heap dumps
06-27 15:19:41.523 9186-9494/fan.fragmentdemo D/LeakCanary: Analysis in progress, working on: PARSING_HEAP_DUMP
06-27 15:19:43.267 9186-9494/fan.fragmentdemo D/LeakCanary: Analysis in progress, working on: EXTRACTING_METADATA
06-27 15:19:43.469 9186-9494/fan.fragmentdemo D/LeakCanary: Analysis in progress, working on: FINDING_RETAINED_OBJECTS
06-27 15:19:43.994 9186-9494/fan.fragmentdemo D/LeakCanary: Analysis in progress, working on: FINDING_PATHS_TO_RETAINED_OBJECTS
06-27 15:19:44.500 9186-9223/fan.fragmentdemo D/LeakCanary: Setting up flushing for Thread[IntentService[HeapAnalyzerService],5,main]
06-27 15:19:47.737 9186-9494/fan.fragmentdemo D/LeakCanary: Analysis in progress, working on: FINDING_DOMINATORS
06-27 15:19:54.663 9186-9494/fan.fragmentdemo D/LeakCanary: Found 2 retained objects
06-27 15:19:54.663 9186-9494/fan.fragmentdemo D/LeakCanary: Analysis in progress, working on: COMPUTING_NATIVE_RETAINED_SIZE
06-27 15:19:55.480 9186-9494/fan.fragmentdemo D/LeakCanary: Analysis in progress, working on: COMPUTING_RETAINED_SIZE
06-27 15:19:55.554 9186-9494/fan.fragmentdemo D/LeakCanary: Analysis in progress, working on: BUILDING_LEAK_TRACES
06-27 15:19:55.558 9186-9494/fan.fragmentdemo D/LeakCanary: Found 2 paths to retained objects, down to 1 after removing duplicated paths
06-27 15:19:55.720 9186-9494/fan.fragmentdemo D/LeakCanary: Analysis in progress, working on: REPORTING_HEAP_ANALYSIS
06-27 15:19:55.737 9186-9494/fan.fragmentdemo D/LeakCanary: ====================================HEAP ANALYSIS RESULT====================================1 APPLICATION LEAKSReferences underlined with "~~~" are likely causes.Learn more at https://squ.re/leaks.30451 bytes retained by leaking objectsSignature: f3466687f84b8cdd14a9862dcc5b72a7115e352b┬───│ GC Root: System class│├─ fan.fragmentdemo.MemoryTestActivity class│ Leaking: NO (a class is never leaking)│ ↓ static MemoryTestActivity.textView2│ ~~~~~~~~~╰→ android.support.v7.widget.AppCompatTextView instance Leaking: YES (ObjectWatcher was watching this because View was detached and View.mContext references a destroyed activity) key = 0f1c40a8-d5be-4253-ab5c-fdec9e64c65d watchDurationMillis = 15965 retainedDurationMillis = 10964 mContext instance of fan.fragmentdemo.MemoryTestActivity with mDestroyed = true View#mParent is set View#mAttachInfo is null (view detached) View.mID = R.id.textView2 View.mWindowAttachCount = 1====================================0 LIBRARY LEAKSA Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over.See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks====================================METADATAPlease include this in bug reports and Stack Overflow questions.Build.VERSION.SDK_INT: 25Build.MANUFACTURER: smartisanLeakCanary version: 2.4App process name: fan.fragmentdemoAnalysis duration: 14196 msHeap dump file path: /storage/emulated/0/Download/leakcanary-fan.fragmentdemo/2020-06-27_15-19-38_530.hprofHeap dump timestamp: 1593242395719====================================
以上是分析hprof文件的日志;
LeakCanary通过Shark解析hprof文件并定位在堆中无法回收被保留的对象;
以上是LeakCanary在堆堆文件找到被保留对象通知;
对于每个保留对象,LeakCanary都会找到防止该保留对象被垃圾回收的引用路径:其泄漏跟踪。下一节将学习分析泄漏跟踪:修复内存泄漏。
以上通知提示在计算被保留对象的引用路径;
分析完成后,LeakCanary会显示一个带有摘要的通知,并在Logcat中打印结果。请注意下面4个保留对象如何分组为2个不同的泄漏。LeakCanary为每个泄漏跟踪创建一个签名,并将具有相同签名的泄漏(即由相同错误引起的泄漏)组合在一起。
以上表示4个引用路径分为两种不同的泄漏签名;
====================================
HEAP ANALYSIS RESULT
====================================
2 APPLICATION LEAKSDisplaying only 1 leak trace out of 2 with the same signature
Signature: ce9dee3a1feb859fd3b3a9ff51e3ddfd8efbc6
┬───
│ GC Root: Local variable in native code
│
...
点击通知可以打开Activity查看更详细的泄漏问题,关闭Activity可以看到LeakCanary加载图标:
以上表示增加了一个为了每个被安装的app增加了一个加载图标;
每一行显示一组有详情签名的内存泄漏问题;LeakCanary标记了一行New表示第一次出现内存泄漏问题;
以上表示4内存泄漏问题分在两行,每行有不同的泄漏签名;
点击打开带有泄漏引用路径。您可以通过下拉菜单在保留对象及其泄漏引用路径之间切换。
以上表示相同泄漏签名的3个内存泄漏问题;
泄漏签名是每个可能导致泄漏的引用的串联的哈希值,即每个引用都用红色下划线显示:
以上引用路径存在三个子引用;
当泄漏的路径被分享做为文本时这些相同的子引用将有下划线~~~
...
│
├─ com.example.leakcanary.LeakingSingleton class
│ Leaking: NO (a class is never leaking)
│ ↓ static LeakingSingleton.leakedViews
│ ~~~~~~~~~~~
├─ java.util.ArrayList instance
│ Leaking: UNKNOWN
│ ↓ ArrayList.elementData
│ ~~~~~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ ↓ Object[].[0]
│ ~~~
├─ android.widget.TextView instance
│ Leaking: YES (View.mContext references a destroyed activity)
...
以上的例子,泄漏的签名将按照如下的方式计算:
val leakSignature = sha1Hash("com.example.leakcanary.LeakingSingleton.leakedView" +"java.util.ArrayList.elementData" +"java.lang.Object[].[x]"
)
println(leakSignature)
// dbfa277d7e5624792e8b60bc950cd164190a11aa
4.对内存泄漏进行分类
LeakCanary在你的app中分两类,Applications Leaks和Library Leaks;一个Library Leak是被第三方库引起的问题(超出你控制范围的);这个leak泄漏影响的应用程序,因为修改它可能不在你控制范围因此LeakCanary把它分开说明;
这两类被分开在Logcat控制台打印:
====================================
HEAP ANALYSIS RESULT
====================================
0 APPLICATION LEAKS====================================
1 LIBRARY LEAK...
┬───
│ GC Root: Local variable in native code
│
...
LeakCanary标记一行在leaks列表中做为Library Leak:
以上表示LeakCanary发现了一个库的内存泄漏问题;
LeakCanary提供了一个已知泄漏的数据库,它通过对引用名称进行模式匹配来识别这些泄漏。例如:
Leak pattern: instance field android.app.Activity$1#this$0
Description: Android Q added a new IRequestFinishCallback$Stub class [...]
┬───
│ GC Root: Global variable in native code
│
├─ android.app.Activity$1 instance
│ Leaking: UNKNOWN
│ Anonymous subclass of android.app.IRequestFinishCallback$Stub
│ ↓ Activity$1.this$0
│ ~~~~~~
╰→ com.example.MainActivity instance
5.其他
我做了什么引起内存泄漏问题?
没问题!您按照预期的方式使用了一个API,但实现中有一个导致此泄漏的bug。
如果阻止内存泄漏问题?
可能!一些库泄漏可以使用反射修复,另一些可以通过使用使泄漏消失的代码路径修复。这种类型的修复往往是黑客,所以小心!您最好的选择可能是找到bug报告或文件,并坚持bug得到修复。
既然我对这次泄漏无能为力,有没有办法让LeakCanary置之不理呢?
在堆文件对其进行分析之前,LeakCanary无法知道泄漏是否是库泄漏。如果在发现库泄漏时,LeakCanary没有显示结果通知,那么您将开始怀疑在toast之后,LeakCanary分析发生了什么。
参考 :
https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/
内存分析工具LeakCanary是如何工作的相关推荐
- Android内存分析工具:Memory Profiler
一.前言 我们知道,Android系统检测到app有不再使用对象时,就会进行内存回收相关的工作. 尽管Android检测无用对象.回收内存的方法在不断改进, 但在目前所有的Android版本中,进 ...
- 内存分析工具MAT的使用
原文链接:http://www.jianshu.com/p/d8e247b1e7b2 MAT简介 MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速. ...
- envi 文件 生成mat_JVM 内存分析工具 MAT 的深度讲解与实践——入门篇
1. MAT 工具简介 MAT(全名:Memory Analyzer Tool),是一款快速便捷且功能强大丰富的 JVM 堆内存离线分析工具.其通过展现 JVM 异常时所记录的运行时堆转储快照(Hea ...
- golang 编写的在线redis 内存分析工具 rma4go
redis 内存分析工具 rma4go redis是一个很有名的内存型数据库,这里不做详细介绍.而rma4go (redis memory analyzer for golang) 是一个redis的 ...
- Volatility内存分析工具-某即时通讯软件Windows端数据库密钥的分析
转载自公众号:取证者联盟 目录 1. 前言 2. 准备工作 3. 内存镜像解析 4. 踩过的坑和感悟 5. 技术要点总结 前言 某年月日,我司在项目中遇到了一个不太常见的需求:根据内存镜像解析电脑中的 ...
- redis 内存分析工具 `rma4go`
redis 内存分析工具 rma4go redis是一个很有名的内存型数据库,这里不做详细介绍.而rma4go (redis memory analyzer for golang) 是一个redis的 ...
- 一个golang编写的redis内存分析工具rma4go
redis 内存分析工具 rma4go 简介 redis是一个很有名的内存型数据库,这里不做详细介绍.而rma4go (redis memory analyzer for golang) 是一个red ...
- redis 内存分析工具 RMA 使用
RMA 介绍 RMA 是一个控制台工具,用于实时扫描 Redis 密钥空间并按密钥模式聚合内存使用情况统计信息.您可以使用此工具而无需在生产服务器上进行维护.您可以按所有或选定的 Redis 类型进行 ...
- Volatility内存分析工具 - 某即时通讯软件Windows端数据库密钥的分析
#本文仅供交流学习使用,切勿用于非法用途# 目录 1. 前言 2. 准备工作 3. 内存镜像解析 4. 踩过的坑和感悟 5. 技术要点总结 前言 某年月日,我司在项目中遇到了一个不太常见的需求:根据内 ...
最新文章
- 集成、知识蒸馏和自蒸馏有区别?
- BIND9配置文件详解模板
- Spring-JDK Timer 以及在Spring(4.0以下)中使用JDK Timer
- 保存课程图片-服务端开发
- C++基础1 数据类型 常量
- 64位进程隐藏不蓝屏_浅析Linux 64位系统虚拟地址和物理地址的映射及验证方法...
- python代码没有反应_没有任何编程经验者不要被Python简明手册误导。
- 又错过了暴富的机会!亚马逊AWS突发Bug,比特币现史诗级捡漏机会?
- Linux下browser-sync无法启动Chrome的解决方法
- Django SCRF跨站点请求伪造
- Jquery中extend的理解以及常见用法
- PCB布线宽度与 mil与mm转换 等技巧
- 步进电机控制和步进电机原理
- Markdown 语法手册 - 完整版(上)
- python数据分析 - T检验与F检验:二组数据那个更好?(一)
- HTML用户名判断,用户名判断(练习)
- java对word文档的操作
- auto auto const auto auto
- 2021年vmware安装archlinux
- 英雄之刃显示服务器断开怎么办,英魂之刃手游新手常见问题