从Matrix-ResourceCanary看内存快照生成-ForkAnalyseProcessor(1)

前文看到AutoDumpProcessor的处理逻辑主要是生成,裁剪hprof文件并回调到PluginListener中,接下来我们来看下ForkAnalyseProcessor的处理逻辑。

ForkAnalyseProcessor

public class ForkAnalyseProcessor extends BaseLeakProcessor {private static final String TAG = "Matrix.LeakProcessor.ForkAnalyse";public ForkAnalyseProcessor(ActivityRefWatcher watcher) {super(watcher);}@Overridepublic boolean process(DestroyedActivityInfo destroyedActivityInfo) {......getWatcher().triggerGc();if (dumpAndAnalyse(destroyedActivityInfo.mActivityName,destroyedActivityInfo.mKey)) {getWatcher().markPublished(destroyedActivityInfo.mActivityName, false);return true;}return false;}private boolean dumpAndAnalyse(String activity, String key) {/* Dump */final long dumpStart = System.currentTimeMillis();File hprof = null;try {hprof = HprofFileManager.INSTANCE.prepareHprofFile("FAP", true);} catch (FileNotFoundException e) {MatrixLog.printErrStackTrace(TAG, e, "");}if (hprof != null) {if (!MemoryUtil.dump(hprof.getPath(), 600)) {MatrixLog.e(TAG, String.format("heap dump for further analyzing activity with key [%s] was failed, just ignore.",key));return false;}}if (hprof == null || hprof.length() == 0) {publishIssue(SharePluginInfo.IssueType.ERR_FILE_NOT_FOUND,ResourceConfig.DumpMode.FORK_ANALYSE,activity, key, "FileNull", "0");MatrixLog.e(TAG, "cannot create hprof file");return false;}MatrixLog.i(TAG, String.format("dump cost=%sms refString=%s path=%s",System.currentTimeMillis() - dumpStart, key, hprof.getPath()));/* Analyse */try {final long analyseStart = System.currentTimeMillis();final ActivityLeakResult leaks = analyze(hprof, key);MatrixLog.i(TAG, String.format("analyze cost=%sms refString=%s",System.currentTimeMillis() - analyseStart, key));if (leaks.mLeakFound) {final String leakChain = leaks.toString();publishIssue(SharePluginInfo.IssueType.LEAK_FOUND,ResourceConfig.DumpMode.FORK_ANALYSE,activity, key, leakChain,String.valueOf(System.currentTimeMillis() - dumpStart));MatrixLog.i(TAG, leakChain);} else {MatrixLog.i(TAG, "leak not found");}} catch (OutOfMemoryError error) {publishIssue(SharePluginInfo.IssueType.ERR_ANALYSE_OOM,ResourceConfig.DumpMode.FORK_ANALYSE,activity, key, "OutOfMemoryError","0");MatrixLog.printErrStackTrace(TAG, error.getCause(), "");} finally {//noinspection ResultOfMethodCallIgnoredhprof.delete();}/* Done */return true;}
}

从上述代码可以看到在ForkAnalyseProcessor中,主要是通过dumpAndAnalyse来处理发现的内存泄漏问题,在该函数内,主要分为以下几步:

  1. prepareHprofFile:创建hprof文件
  2. MemoryUtil.dump:生成hprof文件内容
  3. analyze:分析hprof文件
  4. publishIssue:报告问题
  5. hprof.delete():删除hprof文件

prepareHprofFile

在HprofFileManager.INSTANCE.prepareHprofFile主要是进行hprof文件的预创建工作,包含清理历史文件,确保有足够的存储空间,判断存储空间是否可用,拼接hprof文件名等操作,这里预创建的hprof文件并没有数据内容,prepareHprofFile实现代码如下:

@Throws(FileNotFoundException::class)
fun prepareHprofFile(prefix: String = "", deleteSoon: Boolean = false): File {hprofStorageDir.prepare(deleteSoon)return File(hprofStorageDir, getHprofFileName(prefix))
}

MemoryUtil.dump

MemoryUtil.dump函数主要完成了hprof文件的真实内容填充工作,代码如下所示:

@JvmStatic
@JvmOverloads
fun dump(hprofPath: String,timeout: Long = DEFAULT_TASK_TIMEOUT
): Boolean = initSafe { exception ->if (exception != null) {error("", exception)return@initSafe false}return when (val pid = forkDump(hprofPath, timeout)) {-1 -> run {error("Failed to fork task executing process.")false}else -> run { // current processinfo("Wait for task process [${pid}] complete executing.")val result = waitTask(pid)result.exception?.let {info("Task process [${pid}] complete with error: ${it.message}.")} ?: info("Task process [${pid}] complete without error.")return result.exception == null}}
}private external fun forkDump(hprofPath: String, timeout: Long): Int
private external fun waitTask(pid: Int): TaskResult

可以看到代码中主要逻辑是执行forkDump获取进程id,如果进程id为-1,则返回false,dump失败,如果进程id不为-1,则执行waitTask方法,如果返回的TaskResult对象中没有异常,说明dump成功,否则失败,而forkDump和waitTask都是native方法,接下来我们一起看下这两函数的实现。

forkDump

MemoryUtil.dump对应的native实现如下所示:

extern "C"
JNIEXPORT jint JNICALL
Java_com_tencent_matrix_resource_MemoryUtil_forkDump(JNIEnv *env, jobject,jstring java_hprof_path,jlong timeout) {const std::string hprof_path = extract_string(env, java_hprof_path);int task_pid = fork_task("matrix_mem_dump", timeout);if (task_pid != 0) {return task_pid;} else {/* dump生成hprof文件 */execute_dump(hprof_path.c_str());/* 退出进程 */_exit(TC_NO_ERROR);}
}
static int fork_task(const char *task_name, unsigned int timeout) {auto *thread = current_thread();// 调用art::Dbg::SuspendVM()暂停进程运行suspend_runtime(thread);// fork创建进程int pid = fork();if (pid == 0) {task_process = true;if (timeout != 0) {alarm(timeout);}// 设置线程名称prctl(PR_SET_NAME, task_name);} else {// 调用art::Dbg::ResumeVM()恢复进程运行resume_runtime(thread);}return pid;
}

结合注释,我们可以看出这是一段创建子进程并根据子进程pid运行逻辑的代码,那么这一段代码是怎么执行的呢?

要了解上述代码怎么执行的,我们首先应该清楚fork函数创建子进程的作用和特点,针对fork创建的子进程而言,其和父进程拥有相同的内存空间,fork函数返回值如下所示:

image-20230827113118648

可以看出fork在父进程执行时返回所创建子进程的pid信息,在子进程自身执行时返回0,结合代码,可以得到下图:

forkDump.drawio

接下来我们继续来看下子进程execute_dump和_exit的实现。

execute_dump
static void execute_dump(const char *file_name) {_info_log(TAG, "task_process %d: dump", getpid());update_task_state(TS_DUMP);dump_heap(file_name);
}static void (*dump_heap_)(const char *, int, bool) = nullptr;void dump_heap(const char *file_name) {dump_heap_(file_name, -1, false);
}// xhook
load_symbol(dump_heap_,void(*)(const char *, int, bool ),"_ZN3art5hprof8DumpHeapEPKcib","cannot find symbol art::hprof::DumpHeap()")

可以看到execute_dump最终调用的是art::hprof::DumpHeap()方法,Debug.dumpHprofData最终也是通过jni调用该方法,生成hprof文件。

_exit

image-20230827123154594

结合文档可以看出_exit函数主要用于停止进程运行。

waitTask
extern "C" JNIEXPORT jobject JNICALL
Java_com_tencent_matrix_resource_MemoryUtil_waitTask(JNIEnv *env, jobject, jint pid) {int status;// 通过waitpid等待子进程状态通知if (waitpid(pid, &status, 0) == -1) {_error_log(TAG, "invoke waitpid failed with errno %d", errno);return create_task_result(env, TR_TYPE_WAIT_FAILED, errno, TS_UNKNOWN, "none");}const int8_t task_state = get_task_state_and_cleanup(pid);const std::string task_error = get_task_error_and_cleanup(pid);if (WIFEXITED(status)) {return create_task_result(env, TR_TYPE_EXIT, WEXITSTATUS(status), task_state, task_error);} else if (WIFSIGNALED(status)) {return create_task_result(env, TR_TYPE_SIGNALED, WTERMSIG(status), task_state, task_error);} else {return create_task_result(env, TR_TYPE_UNKNOWN, 0, task_state, task_error);}
}

从waitpid阻塞等待获取到子进程退出状态后,将子进程执行结果包装成TaskResult对象返回。

waitpid

waitpid说明如下图,可以看到waitpid用于阻塞当前线程执行,直到给定的pid关联的子进程状态发生改变时唤醒,唤醒后说明子进程已退出,查看子进程退出原因,并返回结果给上层,至此MemoryUtil.dump流程结束。

image-20230827124402500

image-20230827124551875

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/124559.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nginx 本地部署服务

nginx 部署服务 一、下载二、解压三、文件替换四、浏览器中输入五、离线部署瓦片服务 一、下载 可以到官网下载,官方网址:https://nginx.org/也可以用我发的包 二、解压 三、文件替换 解压打开后文件,双击 nginx.exe 浏览器输入 localhost…

对极几何与三角化求3D空间坐标

一&#xff0c;使用对极几何约束求R,T 第一步&#xff1a;特征匹配。提取出有效的匹配点 void find_feature_matches(const Mat &img_1, const Mat &img_2,std::vector<KeyPoint> &keypoints_1,std::vector<KeyPoint> &keypoints_2,std::vector&l…

Python超入门(1)__迅速上手操作掌握Python

# 1.第一个代码&#xff1a;输出语句 # 1.第一个代码&#xff1a;输出语句 print("My dogs name is Huppy!") print(o----) print( ||| ) print("*" * 10) """ 输出结果&#xff1a; My dogs name is Huppy! o----||| ********** "&…

RK3588算法盒子maskrom模式下系统烧录

先在firefly官网下载bulidroot文件&#xff0c;然后进行烧录相关文件。 点击upgrade&#xff0c;进行烧录升级&#xff1b; 等待烧录完成后&#xff0c; 重新开机进入loader模式下&#xff0c; 进行烧录Untunt20.04系统的操作就行。

(16)线程的实例认识:Await,Async,ConfigureAwait

继续(15)的例子 一、ConfigureAwait()的作用 private async void BtnAsync_Click(object sender, EventArgs e)//异步{Stopwatch sw Stopwatch.StartNew();TxtInfo.Clear();AppendLine("异步检索开始...");AppendLine($"当前线程Id:{Environment.CurrentManage…

springboot使用切面记录接口访问日志

前言 当我们开发和维护一个复杂的应用程序时&#xff0c;了解应用程序的运行情况变得至关重要。特别是在生产环境中&#xff0c;我们需要追踪应用程序的各个方面&#xff0c;以确保它正常运行并能够及时发现潜在的问题。其中之一关键的方面是记录应用程序的接口访问日志。 Sp…

函数相关概念

4.函数 1.函数的概念 1.什么是函数? 把特点的代码片段,抽取成为独立运行的实体 2.使用函数的好处1.重复使用,提供效率2.提高代码的可读性3.有利用程序的维护 3.函数的分类1.内置函数(系统函数)已经提高的alert(); prompt();confirm();print()document.write(),console.log()…

http和https区别,第三方证书如何保证服务器可信

目录 HTTP和HTTPS有以下区别&#xff1a; 第三方证书如何保证服务器的可信性 需要注意哪些方面 可能遇到什么问题 随着互联网技术的不断发展&#xff0c;数据传输的安全性越来越受到人们的关注。HTTP和HTTPS是两种常用的网络协议&#xff0c;它们之间的主要区别在于数据传输…

【图文并茂】c++介绍之队列

1.1队列的定义 队列&#xff08;queue&#xff09;简称队&#xff0c;它也是一种操作受限的线性表&#xff0c;其限制为仅允许在表的一端进行插入操作&#xff0c;而在表的另一端进行删除操作 一些基础概念&#xff1a; 队尾&#xff08;rear&#xff09; &#xff1a;进行插…

CloudQuery X PolarDB:让数据库管理更简单

前言&#xff1a;8 月 15 日&#xff0c;CloudQuery 数据操作管控平台与阿里云 PolarDB 数据库管理软件&#xff0c;完成产品集成认证测试。也在以下功能上完善了用户使用 PolarDB 的体验&#xff0c;使数据库的管理更加安全高效。 支持在 CloudQuery 中创建连接&#xff0c;便…

数据接口工程对接BI可视化大屏(一)

文章目录 第1章 案例概述1.1 案例目标1.2 BI最终效果1.2.1 PC端显示效果1.2.2 移动端显示效果 后记 第1章 案例概述 1.1 案例目标 此项目以常见的手机零售BI场景为例&#xff0c;介绍如何编写数据接口工程对接BI可视化大屏。 如何从当前常见的主流大数据场景中为后台程序推送…

数据结构入门-13-图

文章目录 一、图的概述1.1 图论的作用1.2 图的分类1.2.1 无向图1.2.2 有向图1.2.3 无权图1.2.4 有劝图 1.3 图的基本概念 二、树的基本表示2.1 邻接矩阵2.1.1 邻接矩阵 表示图2.1.2 邻接矩阵的复杂度 2.2 邻接表2.2.1 邻接表的复杂度2.2.2 邻接表By哈希表 三、图的深度优先遍历…

sentinel加密狗使用及规则配置

Sentinel加密狗是一种硬件加密设备&#xff0c;用于保护软件应用程序免受未经授权的访问和复制。它可以提供软件许可管理、访问控制和数据保护等功能。下面是Sentinel加密狗的使用及规则配置的相关介绍。 Sentinel加密狗的使用 插入加密狗&#xff1a;将Sentinel加密狗插入计算…

C51智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结

目录 1.电机模块开发 1.1 让小车动起来 1.2 串口控制小车方向 1.3 如何进行小车PWM调速 1.4 PWM方式实现小车转向 2.循迹小车 2.1 循迹模块使用 2.2 循迹小车原理 2.3 循迹小车核心代码 3.跟随/避障小车 3.1 红外壁障模块分析​编辑 3.2 跟随小车的原理 3.3 跟随小…

Leetcode.174 地下城游戏

题目链接 Leetcode.174 地下城游戏 hard 题目描述 恶魔们抓住了公主并将她关在了地下城 d u n g e o n dungeon dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里&#xff0c;他必须穿过地下城并通过对抗恶魔来拯救公…

【高阶产品策略】设计有效的AB测试

文章目录 1、A/B测试概述2、A/B测试实施过程3、A/B测试中需要注意的地方4、从一个案例中看A/B测试 1、A/B测试概述 2、A/B测试实施过程 3、A/B测试中需要注意的地方 4、从一个案例中看A/B测试

编写中间件以用于 Express 应用程序

概述 中间件函数能够访问请求对象 (req)、响应对象 (res) 以及应用程序的请求/响应循环中的下一个中间件函数。下一个中间件函数通常由名为 next 的变量来表示。 中间件函数可以执行以下任务&#xff1a; 执行任何代码。对请求和响应对象进行更改。结束请求/响应循环。调用堆…

pytorch学习——循环神经网络RNN讲解及其实现

参考书籍&#xff1a;8.6. 循环神经网络的简洁实现 — 动手学深度学习 2.0.0 documentation 参考视频&#xff1a;54 循环神经网络 RNN【动手学深度学习v2】_哔哩哔哩_bilibili 一.介绍 循环神经网络RNN&#xff08;Recurrent Neural Network &#xff09;是一类广泛应用于序列…

stm32之30.DMA

DMA&#xff08;硬件加速方法&#xff09;一般用于帮运比较大的数据&#xff08;如&#xff1a;摄像头数据图像传输&#xff09;&#xff0c;寄存器-》DMA-》RAM 或者 RAM-》DMA-》寄存器提高CPU的工作效率 源码-- #include "myhead.h" #include "adc.h"#…

javaee之黑马乐优商城2

简单分析一下商品分类表的结构 先来说一下分类表与品牌表之间的关系 再来说一下分类表和品牌表与商品表之间的关系 面我们要开始就要创建sql语句了嘛&#xff0c;这里我们分析一下字段 用到的数据库是heima->tb_category这个表 现在去数据库里面创建好这张表 下面我们再去编…