LeakCanary 源码详解(3)
上一篇:LeakCanary源码详解(2) 如果你是直接刷到这篇的,建议还是从1开始看,然后2,然后是这篇3,如果你只关注这篇的重点hprof 文件定位泄漏位置的感兴趣,可以试试直接读这篇,如果中间没发觉有难理解的就算了,要是发觉无法理解了就建议从1 2篇读起,经典的库的代码没那么简单,不要害怕花时间。
这篇要说一下hprof解析的,下图中是dumpHeap反复中,有两次sendEvent
我们看一下里面
eventListeners里面的RemoteWorkManagerHeapAnalyzer就是分析hprof的类
然后就是RemoteHeapAnalyzerWorker
核心就是AndroidDebugHeapAnalyzer.runAnalysisBlocking了
然后是analyzeheap()方法
里面是调用heapAnalyzer.analyze方法
又调用了类本身内部的analyze方法。
调用了helpers.analyzeGraph()
而在202309/24 新版本上是这图这样的,结构变了,逻辑一样,分析 graph
2.2 Hprof 文件解析
解析入口:
//HeapAnalyzerService
private fun analyzeHeap(
heapDumpFile: File,
config: Config
): HeapAnalysis {
val heapAnalyzer = HeapAnalyzer(this)
val proguardMappingReader = try {//解析混淆文件ProguardMappingReader(assets.open(PROGUARD_MAPPING_FILE_NAME))
} catch (e: IOException) {null
}
//分析hprof文件
return heapAnalyzer.analyze(heapDumpFile = heapDumpFile,leakingObjectFinder = config.leakingObjectFinder,referenceMatchers = config.referenceMatchers,computeRetainedHeapSize = config.computeRetainedHeapSize,objectInspectors = config.objectInspectors,metadataExtractor = config.metadataExtractor,proguardMapping = proguardMappingReader?.readProguardMapping()
)
}
关于Hprof文件的解析细节,就需要牵扯到Hprof二进制文件协议:
http://hg.openjdk.java.net/jdk6/jdk6/jdk/raw-file/tip/src/share/demo/jvmti/hprof/manual.html#mozTocId848088
通过阅读协议文档,hprof的二进制文件结构大概如下:
解析流程:
fun analyze(
heapDumpFile: File,
leakingObjectFinder: LeakingObjectFinder,
referenceMatchers: List = emptyList(),
computeRetainedHeapSize: Boolean = false,
objectInspectors: List = emptyList(),
metadataExtractor: MetadataExtractor = MetadataExtractor.NO_OP,
proguardMapping: ProguardMapping? = null
): HeapAnalysis {
val analysisStartNanoTime = System.nanoTime()
if (!heapDumpFile.exists()) {
val exception = IllegalArgumentException(“File does not exist: $heapDumpFile”)
return HeapAnalysisFailure(
heapDumpFile, System.currentTimeMillis(), since(analysisStartNanoTime),
HeapAnalysisException(exception)
)
}
return try {
listener.onAnalysisProgress(PARSING_HEAP_DUMP)
Hprof.open(heapDumpFile)
.use { hprof ->
val graph = HprofHeapGraph.indexHprof(hprof, proguardMapping)//建立gragh
val helpers =
FindLeakInput(graph, referenceMatchers, computeRetainedHeapSize, objectInspectors)
helpers.analyzeGraph(//分析graph
metadataExtractor, leakingObjectFinder, heapDumpFile, analysisStartNanoTime
)
}
} catch (exception: Throwable) {
HeapAnalysisFailure(
heapDumpFile, System.currentTimeMillis(), since(analysisStartNanoTime),
HeapAnalysisException(exception)
)
}
}
LeakCanary在建立对象实例Graph时,主要解析以下几种tag:
涉及到的GCRoot对象有以下几种:
2.2.1 构建内存索引(Graph内容索引)
LeakCanary会根据Hprof文件构建一个HprofHeapGraph 对象,该对象记录了以下成员变量:
interface HeapGraph {
val identifierByteSize: Int
/**
- In memory store that can be used to store objects this [HeapGraph] instance.
/
val context: GraphContext
/* - All GC roots which type matches types known to this heap graph and which point to non null
- references. You can retrieve the object that a GC Root points to by calling [findObjectById]
- with [GcRoot.id], however you need to first check that [objectExists] returns true because
- GC roots can point to objects that don’t exist in the heap dump.
/
val gcRoots: List
/* - Sequence of all objects in the heap dump.
- This sequence does not trigger any IO reads.
*/
val objects: Sequence //所有对象的序列,包括类对象、实例对象、对象数组、原始类型数组
val classes: Sequence //类对象序列
val instances: Sequence //实例对象数组
val objectArrays: Sequence //对象数组序列
val primitiveArrays: Sequence //原始类型数组序列
}
为了方便快速定位到对应对象在hprof文件中的位置,LeakCanary提供了内存索引HprofInMemoryIndex :
建立字符串索引hprofStringCache(Key-value):key是字符ID,value是字符串;
作用: 可以根据类名,查询到字符ID,也可以根据字符ID查询到类名。
建立类名索引classNames(Key-value):key是类对象ID,value是类字符串ID;
作用: 根据类对象ID查询类字符串ID。
建立实例索引**instanceIndex(**Key-value):key是实例对象ID,value是该对象在hprof文件中的位置以及类对象ID;
作用: 快速定位实例的所处位置,方便解析实例字段的值。
建立类对象索引classIndex(Key-value):key是类对象ID,value是其他字段的二进制组合(父类ID、实例大小等等);
作用: 快速定位类对象的所处位置,方便解析类字段类型。
建立对象数组索引objectArrayIndex(Key-value):key是类对象ID,value是其他字段的二进制组合(hprof文件位置等等);
作用: 快速定位对象数组的所处位置,方便解析对象数组引用的对象。
建立原始数组索引primitiveArrayIndex(Key-value):key是类对象ID,value是其他字段的二进制组合(hprof文件位置、元素类型等等);
2.2.2 找到泄漏的对象
1)由于需要检测的对象被
com.squareup.leakcanary.KeyedWeakReference 持有,所以可以根据
com.squareup.leakcanary.KeyedWeakReference 类名查询到类对象ID;
- 解析对应类的实例域,找到字段名以及引用的对象ID,即泄漏的对象ID;
2.2.3找到最短的GCRoot引用链
根据解析到的GCRoot对象和泄露的对象,在graph中搜索最短引用链,这里采用的是广度优先遍历的算法进行搜索的:
//PathFinder
private fun State.findPathsFromGcRoots(): PathFindingResults {
enqueueGcRoots()//1
val shortestPathsToLeakingObjects = mutableListOf<ReferencePathNode>()
visitingQueue@ while (queuesNotEmpty) {val node = poll()//2if (checkSeen(node)) {//2throw IllegalStateException("Node $node objectId=${node.objectId} should not be enqueued when already visited or enqueued")}if (node.objectId in leakingObjectIds) {//3shortestPathsToLeakingObjects.add(node)// Found all refs, stop searching (unless computing retained size)if (shortestPathsToLeakingObjects.size == leakingObjectIds.size) {//4if (computeRetainedHeapSize) {listener.onAnalysisProgress(FINDING_DOMINATORS)} else {break@visitingQueue}}}when (val heapObject = graph.findObjectById(node.objectId)) {//5is HeapClass -> visitClassRecord(heapObject, node)is HeapInstance -> visitInstance(heapObject, node)is HeapObjectArray -> visitObjectArray(heapObject, node)}
}
return PathFindingResults(shortestPathsToLeakingObjects, dominatedObjectIds)
}
1)GCRoot对象都入队;
2)队列中的对象依次出队,判断对象是否访问过,若访问过,则抛异常,若没访问过则继续;
3)判断出队的对象id是否是需要检测的对象,若是则记录下来,若不是则继续;
4)判断已记录的对象ID数量是否等于泄漏对象的个数,若相等则搜索结束,相反则继续;
5)根据对象类型(类对象、实例对象、对象数组对象),按不同方式访问该对象,解析对象中引用的对象并入队,并重复2)。
入队的元素有相应的数据结构ReferencePathNode ,原理是链表,可以用来反推出引用链。
findShortestPathsFromGcRoots 是查找泄漏对象到Gcroot最短路径,这个我和其他同类文章认为的不同,他们觉得是只有最短路径才能被直接访问到,而其他更长的路径引用中可能包含其他非直接引用的对象,这些对象不可能是泄漏点。我没法理解这样的解释,我个人理解是这个算法用的是广度优先算法(因为与深度优先算法比,广度优先的长处是速度快,缺点是占用空间),从下面一层一层的查,找到最近最短的路径,这个肯定是泄漏的,是要我们处理的。而其他引用路径就算有也是下一次去解决(这种情况从概率上说是很小的),也就是就算有也会在下一次找到,在下一次还是最短路径。这种算法是保证最快的找到泄漏的引用链(反正你所有的泄漏都是要处理的,按这个顺序能保证最快)。
下图就是我们使用的时候收到的通知消息,可见一斑。
就先到这里吧,有时间再把里面的细节梳理一下吧。
相关文章:
![](https://img-blog.csdnimg.cn/2b3e166f2a8b46b3b8d14cfdbc982752.png)
LeakCanary 源码详解(3)
上一篇:LeakCanary源码详解(2) 如果你是直接刷到这篇的,建议还是从1开始看,然后2,然后是这篇3,如果你只关注这篇的重点hprof 文件定位泄漏位置的感兴趣,可以试试直接读这篇ÿ…...
![](https://www.ngui.cc/images/no-images.jpg)
springboot使用SSE
1、pom文件 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency> 2、前端代码 <!DOCTYPE html> <html lang"en"> <head><meta ch…...
![](https://img-blog.csdnimg.cn/1825aa35998e493481a49597be750f45.png#pic_center)
搞定ESD(一):静电放电测试标准解析
文章目录 一、基本术语与定义1.1 基本定义1.2 重要基本术语 二、静电放电发生器介绍2.1 静电放电发生器的特性:通用规范【GB/T17626.2-2018 标准】2.2 ESD 放电发生器电极规格要求:通用规范【GB/T17626.2-2018 标准】2.3 放电回路电缆的要求:…...
![](https://www.ngui.cc/images/no-images.jpg)
问界M7的诸多优点(自动驾驶走进我们的生活二)
博主一直在问界工厂工作,从未对自己工厂的车如此关注过;但问界系列上市后,经常在茶余饭后看B站视频,发现问界车越来越多不可比拟的优点如下: 一、绿牌 绿牌特权在重庆可以随时过桥,不受限号限制。 二、增…...
![](https://www.ngui.cc/images/no-images.jpg)
[运维|数据库] msql中的 FIND_IN_SET如何转化为pg数据中的ARRAY_POSITION的函数
在 MySQL 中,FIND_IN_SET 函数用于查找一个值是否存在于逗号分隔的字符串列表中。在 PostgreSQL 中,可以使用 string_to_array 函数将逗号分隔的字符串转换为数组,然后使用 ARRAY_POSITION 函数来查找值是否在数组中。 以下是如何将MySQL中的…...
![](https://www.ngui.cc/images/no-images.jpg)
LeetCode 面试题 05.03. 翻转数位
文章目录 一、题目二、Java 题解 一、题目 给定一个32位整数 num,你可以将一个数位从0变为1。请编写一个程序,找出你能够获得的最长的一串1的长度。 示例 1: 输入: num 1775(110111011112) 输出: 8 示例 2: 输入: num 7(01112)…...
![](https://img-blog.csdnimg.cn/492d7612ecf9402ca45f3dad0b4d69e3.png#pic_center)
Fiddler抓包工具配置+Jmeter基本使用
一、Fiddler抓包工具的配置和使用 在编写网关自动化脚本之前,得先学会如何抓包,这里以Fiddler为例。会抓包的同学可以跳过这一步,当然看看也是没坏处的…… 局域网络配置 将要进行抓包的手机与电脑连入同一局域网,电脑才能够抓到…...
![](https://img-blog.csdnimg.cn/d9b53f90f3f4415ba9ade305823c28ca.png#pic_center)
IOTE 2023国际物联网展直击:芯与物发布全新定位芯片,助力多领域智能化发展
IOTE 2023国际物联网展,作为全球物联网领域的盛会,于9月20日在中国深圳拉开帷幕。北斗星通集团应邀参展,旗下专业从事物联网、消费类GNSS芯片研发设计的芯与物公司也随其亮相本届盛会。 展会上,芯与物展示了一系列创新的GNSS定位…...
![](https://img-blog.csdnimg.cn/c8dce2e76fac469e91d42bfe21f4e191.png#pic_center)
【软件设计师-从小白到大牛】上午题基础篇:第二章 操作系统
文章目录 前言章节提要一、进程管理1、进程的状态2、前趋图3、进程的同步与互斥4、PV操作6、PV操作与前趋图7、死锁问题进程资源图(补充)真题链接 二、存储管理1、分区存储组织2、页式存储组织3、段式存储组织4、段页式存储组织5、快表6、页面置换算法单…...
![](https://img-blog.csdnimg.cn/5f61b20f5da949749b51897d611ac04a.png)
【20230921】关于sing-box命令行程序开机自启动运行(Windows、Linux)
1 背景 sing-box是一个命令行程序,官网给出的教程是复制链接到Git Bash(windows)或终端运行(Linux)。每次开机都进行复制运行是一件繁琐的事情。 复制的内容其实就是下次并运行shell脚本,其实系统只需要运…...
![](https://img-blog.csdnimg.cn/cdc5499a8af044c2bc6240576b00e7a2.png)
LeetCode 75-02:字符串的最大公因子
前置知识:使用欧几里得算法求出最大公约数 func gcdOfStrings(str1 string, str2 string) string {if str1str2 ! str2str1 {return ""}return str1[:gcd(len(str1), len(str2))] }func gcd(a, b int)int{if b 0{return a}return gcd(b, a%b) }...
![](https://www.ngui.cc/images/no-images.jpg)
k8s1.19使用ceph14
一、静态 pv (rbd)方式 1、所有k8s节点安装依赖组件 注意:安装ceph-common软件包推荐使用软件包源与Ceph集群源相同,软件版本一致。 cat > /etc/yum.repos.d/ceph.repo << EOF [ceph] name=ceph baseurl=http://mirrors.aliyun.com/ceph/rpm-nautilus/el7/x86_…...
![](https://img-blog.csdnimg.cn/0b84606ea5294de39e0101a98b2a876e.png)
Leetcode 50. Pow(x, n)
文章目录 题目代码(9.19 首刷看解析) 题目 Leetcode 50. Pow(x, n) 代码(9.19 首刷看解析) 快速幂 class Solution { public:double myPow(double x, int n) {if(n 0)return 1;if(n 1)return x;if(n INT_MIN) { // 避免-n整…...
![](https://www.ngui.cc/images/no-images.jpg)
hive分区表的元数据信息numRows显示为0
创建分区表 CREATE TABLE `dept_partition`(`deptno` int, `dname` string, `loc` string) PARTITIONED BY (...
![](https://img-blog.csdnimg.cn/e24490d519d743be8e351cf21743c930.png)
Baumer工业相机堡盟工业相机如何通过BGAPI SDK设置相机的图像剪切(ROI)功能(C++)
Baumer工业相机堡盟工业相机如何通过BGAPI SDK设置相机的图像剪切(ROI)功能(C) Baumer工业相机Baumer工业相机的图像剪切(ROI)功能的技术背景CameraExplorer如何使用图像剪切(ROI)功…...
![](https://img-blog.csdnimg.cn/45531fc971244b1f89eadf8477040852.png)
【云原生】聊聊为什么需要docker以及其基础架构
为什么需要docker 在没有docker之前,我们开发、测试、生产其实是根据不同的服务器进行配置的,很可能因为软件配置不同而导致的生产事故,那么如果能较好的解决软件和配置等封装成一个可运行的软件,无需关注配置,那么是…...
![](https://img-blog.csdnimg.cn/c12207c390eb4fdf8cac2b6e45b174e1.png)
“高级前端开发技术探索路由的使用及Node安装使用“
目录 引言1. Vue路由的使用2. VueNode.js的安装使用总结 引言 在当今互联网时代,前端开发技术日新月异,不断涌现出各种新的框架和工具。作为一名前端开发者,我们需要不断学习和探索新的技术,以提升自己的开发能力。本文将深入探讨…...
![](https://img-blog.csdnimg.cn/0064f1f47ab64ed1a2f762e8616ad53e.png)
LeetCode 494.目标和 (动态规划 + 性能优化)二维数组 压缩成 一维数组
494. 目标和 - 力扣(LeetCode) 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - ,然后串联起所有整数,可以构造一个 表达式 : 例如,nums [2, 1] ,可以在 2…...
![](https://img-blog.csdnimg.cn/img_convert/3fadcfd14189c6dc2182b96b0926997f.png)
[36c3 2019]includer
[36c3 2019]includer 题目描述:Just sitting here and waiting for PHP 8.0 (lolphp). 首先来了解一下临时文件包含之PHP - compress.zlib:// 在 php-src 里可以找到和 compress.zlib:// 有关的代码 | code 注意到 STREAM_WILL_CAST,涉及到 cast 经常…...
![](https://www.ngui.cc/images/no-images.jpg)
Python150题day10
④continue练习 从列表 Ist [1,3,5,2,7,9,10] 中输出所有的奇数,代码如下 lst [1, 3, 5, 2, 7, 9, 10] for item in lst: if item % 2 0: continue print(item) 在上述代码中,当遇到偶数时,continue 语句会跳过当前迭代&…...
![](https://www.ngui.cc/images/no-images.jpg)
Autosar工具-Davinci Developer
文章目录 前言一、Davinci Developer简介二、导航栏File(主要是用于保存、打开工程等操作)HomeProject(主要用于导入、导出arxml文件)Graphic(主要在SWC设计时使用,包含对图形界面下的设计工具)Window(主要就是对我们的Dev界面外形修改用的,使得界面更加方便我们使用(比如隐…...
![](https://img-blog.csdnimg.cn/8fc9c306206240b5aa152d95e23b98d6.png)
js中的数据结构:栈,队列,链表,字典哈希表,树
栈:先进后出 队列:先进先出 链表: 单链表: 双链表: 环形链表:最后一个数据的next指针不是指向null,指向的是任意之间的一个数据,形成一个环 数组和链表的区别: 字典和哈…...
![](https://img-blog.csdnimg.cn/300cb1e1b4db4cd28935406a6a199dac.png)
Verdi实现信号的平移
在Verilog/System verilog中,# xxx可以实现延迟指定时间的功能,而在使用verdi查看信号波形并进行分析时,同样也可以实现类似的功能。 (注:这种信号平移是有其应用场景的,例如,在某些仿真模型中,…...
![](https://img-blog.csdnimg.cn/1d3e0af16efd44c383e16be588ef7dcb.png)
Leetcode算法入门与数组丨6. 数组双指针、滑动窗口
文章目录 1 双指针基础知识1.1 双指针简介1.2 左右指针(对撞指针)1.3 快慢指针1.4 分离双指针 2 滑动窗口基础知识2.1 滑动窗口算法介绍2.2 滑动窗口适用范围2.3 固定长度滑动窗口2.4 不固定长度滑动窗口 1 双指针基础知识 1.1 双指针简介 双指针&…...
![](https://img-blog.csdnimg.cn/9095164842fd4015830ff220218ad179.png)
推荐一本书《横向领导力》
大家好,这里是大话硬件。 今天想给大家推荐一本我近期正在阅读的书籍《横向领导力》。 这本书很早就买了,但是在去年就看了前面3章的内容,而且也没做笔记,仅仅是在书本上写写画画,也没有什么体会,感觉看不懂…...
![](https://www.ngui.cc/images/no-images.jpg)
React实战过程的知识了解
做项目用到react和antd,没办法循序渐进的学习,只能把一些点记录在这里,希望大家指正。 1.杂七杂八 正文 //actionRef,操作表单的预设方法,包括:刷新、重置所有项并刷新、重置到默认项、加载更多、清空选…...
![](https://www.ngui.cc/images/no-images.jpg)
F对象和Q对象
F对象和Q对象 F对象 一个F对象代表数据库中某条记录的字段的信息 作用: 通常是对数据库中的字段值在不获取的情况下进行操作 用于类属性(字段)之间的比较 语法 from django.db.models import F F(列名)解决一种极端事件的产生,比如用户对一条微博的点赞…...
![](https://img-blog.csdnimg.cn/428bdeea79294607a2caddcb503c353b.png)
Visio——绘制倾斜线段
一、形状 -> 图表和数学图形 -> 多行 二、放置多行线,可以发现存在两个折点 三、选择多行线,右键选择删除点,即可得到倾斜线段...
![](https://img-blog.csdnimg.cn/da0c6927c9af43838aea27847f337ad3.png)
Linux复习-安装与熟悉环境(一)
这里写目录标题 虚拟机ubuntu系统配置镜像Linux命令vi编辑器3个模式光标命令vi模式切换命令vi拷贝与粘贴命令vi保存和退出命令vi的查找命令vi替换命令 末行模式复制、粘贴、剪切gcc编译器 虚拟机 VMware16 官网下载:vmware官网 网盘下载: 链接ÿ…...
![](https://www.ngui.cc/images/no-images.jpg)
Go基础语法:map
9 map Go 语言中提供的映射关系容器为 map ,其内部使用 散列表(hash) 实现。它是一种无序的基于 key-value 的数据结构。 Go 语言中的 map 是引用类型,必须初始化之后才能使用。 9.1 map 定义 Go 语言中 map 的定义语法为&…...
![](/images/no-images.jpg)
网站 做百度推广有没有效果怎么样/互联网行业都有哪些工作
背景 为了仿真性能的提高,我们可以在编译时使用-debug_access选项,来更好地控制仿真中调试的功能。与-debug_pp选项相比,-debug_access选项允许存储VPD和FSDB文件以进行后处理调试,并减少不必要的调试选项。可以使用-debug_access…...
![](https://img-blog.csdnimg.cn/img_convert/803978759418134cfaa90d7bd591d17e.png)
wordpress特色图像地址/网店推广渠道有哪些
这几天在寒潮的影响下,全国多个地区温度骤降,已经好多年没有感受到这样严酷的冬季了,暖气都撑不住。零下十多度的天气不仅影响人的生活,现在电脑也中招了,确切说是散热——有网友发了一个电脑水冷被冻住的视频…...
![](https://img-blog.csdnimg.cn/img_convert/a5aadda31ae6491e8c82cb339af610df.png)
长春网站建设SEO优化营销/销售外包公司
原文链接:https://dev.to/brunooliveira/rust-understanding-traits-1-45md原文标题:Rust - understanding traits 1公众号:Rust 碎碎念概述(Introduction)如今很多语言都是面向对象,支持这种范式,或者不是直接面向对象…...
![](https://www.oschina.net/img/hot3.png)
牡丹花网站建设策划书/百度秒收录排名软件
2019独角兽企业重金招聘Python工程师标准>>> 第一步:定义两个类 Task3 package group.esperanto.util;import java.text.SimpleDateFormat; import java.util.Date;import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereo…...
![](/images/no-images.jpg)
可上传多个视频的网站建设/怎么找专业的营销团队
1、Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC就容易实现restful url,而struts2的架构实现起来要费…...
![](/images/no-images.jpg)
网站建设多少钱信息/智能建站网站模板
微信小程序中的空格怎么打 文章目录微信小程序中的空格怎么打怎么打呢?解决怎么打呢? 微信小程序中的多个空格怎么打?  不行。 解决 复 制 吧 前面每个字之间都有一个空白字符,🐮space...