一旦LeakCanary被安装,它自动检测和报告内存泄漏,分4步:

  1. 检测保留下来的对象;
  2. 导出堆信息;
  3. 分析堆信息;
  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是如何工作的相关推荐

  1. Android内存分析工具:Memory Profiler

    一.前言  我们知道,Android系统检测到app有不再使用对象时,就会进行内存回收相关的工作. 尽管Android检测无用对象.回收内存的方法在不断改进,  但在目前所有的Android版本中,进 ...

  2. 内存分析工具MAT的使用

    原文链接:http://www.jianshu.com/p/d8e247b1e7b2 MAT简介 MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速. ...

  3. envi 文件 生成mat_JVM 内存分析工具 MAT 的深度讲解与实践——入门篇

    1. MAT 工具简介 MAT(全名:Memory Analyzer Tool),是一款快速便捷且功能强大丰富的 JVM 堆内存离线分析工具.其通过展现 JVM 异常时所记录的运行时堆转储快照(Hea ...

  4. golang 编写的在线redis 内存分析工具 rma4go

    redis 内存分析工具 rma4go redis是一个很有名的内存型数据库,这里不做详细介绍.而rma4go (redis memory analyzer for golang) 是一个redis的 ...

  5. Volatility内存分析工具-某即时通讯软件Windows端数据库密钥的分析

    转载自公众号:取证者联盟 目录 1. 前言 2. 准备工作 3. 内存镜像解析 4. 踩过的坑和感悟 5. 技术要点总结 前言 某年月日,我司在项目中遇到了一个不太常见的需求:根据内存镜像解析电脑中的 ...

  6. redis 内存分析工具 `rma4go`

    redis 内存分析工具 rma4go redis是一个很有名的内存型数据库,这里不做详细介绍.而rma4go (redis memory analyzer for golang) 是一个redis的 ...

  7. 一个golang编写的redis内存分析工具rma4go

    redis 内存分析工具 rma4go 简介 redis是一个很有名的内存型数据库,这里不做详细介绍.而rma4go (redis memory analyzer for golang) 是一个red ...

  8. redis 内存分析工具 RMA 使用

    RMA 介绍 RMA 是一个控制台工具,用于实时扫描 Redis 密钥空间并按密钥模式聚合内存使用情况统计信息.您可以使用此工具而无需在生产服务器上进行维护.您可以按所有或选定的 Redis 类型进行 ...

  9. Volatility内存分析工具 - 某即时通讯软件Windows端数据库密钥的分析

    #本文仅供交流学习使用,切勿用于非法用途# 目录 1. 前言 2. 准备工作 3. 内存镜像解析 4. 踩过的坑和感悟 5. 技术要点总结 前言 某年月日,我司在项目中遇到了一个不太常见的需求:根据内 ...

最新文章

  1. 集成、知识蒸馏和自蒸馏有区别?
  2. BIND9配置文件详解模板
  3. Spring-JDK Timer 以及在Spring(4.0以下)中使用JDK Timer
  4. 保存课程图片-服务端开发
  5. C++基础1 数据类型 常量
  6. 64位进程隐藏不蓝屏_浅析Linux 64位系统虚拟地址和物理地址的映射及验证方法...
  7. python代码没有反应_没有任何编程经验者不要被Python简明手册误导。
  8. 又错过了暴富的机会!亚马逊AWS突发Bug,比特币现史诗级捡漏机会?
  9. Linux下browser-sync无法启动Chrome的解决方法
  10. Django SCRF跨站点请求伪造
  11. Jquery中extend的理解以及常见用法
  12. PCB布线宽度与 mil与mm转换 等技巧
  13. 步进电机控制和步进电机原理
  14. Markdown 语法手册 - 完整版(上)
  15. python数据分析 - T检验与F检验:二组数据那个更好?(一)
  16. HTML用户名判断,用户名判断(练习)
  17. java对word文档的操作
  18. auto auto const auto auto
  19. 2021年vmware安装archlinux
  20. 英雄之刃显示服务器断开怎么办,英魂之刃手游新手常见问题

热门文章

  1. Java奇怪的位移_Java中位移的疑惑
  2. java poi打印excel_POI打印Excel报表
  3. 树莓派WEB服务器(Boa)CGI编程入门
  4. Qml自定义等待指示器
  5. 星空璀璨,时光流逝,分享技术,记录生活——2016年11月22日
  6. BZOJ4372: 烁烁的游戏(动态点分治)
  7. FFmpeg的HEVC解码器源代码简单分析:解析器(Parser)部分
  8. FFmpeg的HEVC解码器源代码简单分析:CTU解码(CTU Decode)部分-PU
  9. 定时清理服务器日志文件
  10. java五子棋棋盘_java绘制五子棋棋盘代码示例