【Android 内存优化】KOOM线程泄漏监控的实现源码分析
文章目录
- 线程monitor的流程
- 怎么判断线程是否泄漏
- AddThread
- JoinThread
- ExitThread
- DetachThread
- 总结
前面我们通过研究KOOM的开源代码,研究了关于Java层和native层内存泄漏监控的实现原理。还剩下线程泄漏这部分没有进行分析,今天来补全它。整体下来,相信我们对于内存监控在代码上的实现上会有一个较为体系化的了解。
线程monitor的流程
从开启monitor的startLoop方法开始:
override fun call(): LoopState {handleThreadLeak()return LoopState.Continue
}
一路进入方法栈,到了这里:
void Refresh() {auto info = new SimpleHookInfo(Util::CurrentTimeNs());sHookLooper->post(ACTION_REFRESH, info);
}
看下sHookLooper类:
namespace koom {
class HookLooper : public looper {public:koom::ThreadHolder *holder;HookLooper();~HookLooper();void handle(int what, void *data);void post(int what, void *data);
};
根据ACTION_REFRESH来看看有哪些action:
enum HookAction {ACTION_ADD_THREAD,ACTION_START_THREAD,ACTION_JOIN_THREAD,ACTION_EXIT_THREAD,ACTION_DETACH_THREAD,ACTION_INIT,ACTION_REFRESH,ACTION_SET_NAME,
};
根据action的处理,找到了handler处理message的地方:
namespace koom {
const char *looper_tag = "koom-hook-looper";
HookLooper::HookLooper() : looper() { this->holder = new koom::ThreadHolder(); }
HookLooper::~HookLooper() { delete this->holder; }
void HookLooper::handle(int what, void *data) {looper::handle(what, data);switch (what) {case ACTION_ADD_THREAD: {koom::Log::info(looper_tag, "AddThread");auto info = static_cast<HookAddInfo *>(data);holder->AddThread(info->tid, info->pthread, info->is_thread_detached,info->time, info->create_arg);delete info;break;}case ACTION_JOIN_THREAD: {koom::Log::info(looper_tag, "JoinThread");auto info = static_cast<HookInfo *>(data);holder->JoinThread(info->thread_id);delete info;break;}case ACTION_DETACH_THREAD: {koom::Log::info(looper_tag, "DetachThread");auto info = static_cast<HookInfo *>(data);holder->DetachThread(info->thread_id);delete info;break;}case ACTION_EXIT_THREAD: {koom::Log::info(looper_tag, "ExitThread");auto info = static_cast<HookExitInfo *>(data);holder->ExitThread(info->thread_id, info->threadName, info->time);delete info;break;}case ACTION_REFRESH: {koom::Log::info(looper_tag, "Refresh");auto info = static_cast<SimpleHookInfo *>(data);holder->ReportThreadLeak(info->time);delete info;break;}default: {}}
}
void HookLooper::post(int what, void *data) { looper::post(what, data); }
} // namespace koom
可以发现不同线程相关的操作都进行了处理。
以HookThreadStart为例,看看发送这个message的地方:
ALWAYS_INLINE void ThreadHooker::HookThreadStart(void *arg) {koom::Log::info(thread_tag, "HookThreadStart");auto *hookArg = (StartRtnArg *)arg;pthread_attr_t attr;pthread_t self = pthread_self();int state = 0;if (pthread_getattr_np(self, &attr) == 0) {pthread_attr_getdetachstate(&attr, &state);}int tid = (int)syscall(SYS_gettid);koom::Log::info(thread_tag, "HookThreadStart %p, %d, %d", self, tid,hookArg->thread_create_arg->stack_time);auto info = new HookAddInfo(tid, Util::CurrentTimeNs(), self,state == PTHREAD_CREATE_DETACHED,hookArg->thread_create_arg);sHookLooper->post(ACTION_ADD_THREAD, info);void *(*start_rtn)(void *) = hookArg->start_rtn;void *routine_arg = hookArg->arg;delete hookArg;start_rtn(routine_arg);
}
这个方法被HookThreadCreate方法调用:
int ThreadHooker::HookThreadCreate(pthread_t *tidp, const pthread_attr_t *attr,void *(*start_rtn)(void *), void *arg) {if (hookEnabled() && start_rtn != nullptr) {auto time = Util::CurrentTimeNs();koom::Log::info(thread_tag, "HookThreadCreate");auto *hook_arg = new StartRtnArg(arg, Util::CurrentTimeNs(), start_rtn);auto *thread_create_arg = hook_arg->thread_create_arg;void *thread = koom::CallStack::GetCurrentThread();if (thread != nullptr) {koom::CallStack::JavaStackTrace(thread,hook_arg->thread_create_arg->java_stack);}koom::CallStack::FastUnwind(thread_create_arg->pc,koom::Constant::kMaxCallStackDepth);thread_create_arg->stack_time = Util::CurrentTimeNs() - time;return pthread_create(tidp, attr,reinterpret_cast<void *(*)(void *)>(HookThreadStart),reinterpret_cast<void *>(hook_arg));}return pthread_create(tidp, attr, start_rtn, arg);
}
HookThreadCreate又被RegisterSo调用。
bool ThreadHooker::RegisterSo(const std::string &lib, int source) {if (IsLibIgnored(lib)) {return false;}auto lib_ctr = lib.c_str();koom::Log::info(thread_tag, "HookSo %d %s", source, lib_ctr);xhook_register(lib_ctr, "pthread_create",reinterpret_cast<void *>(HookThreadCreate), nullptr);xhook_register(lib_ctr, "pthread_detach",reinterpret_cast<void *>(HookThreadDetach), nullptr);xhook_register(lib_ctr, "pthread_join",reinterpret_cast<void *>(HookThreadJoin), nullptr);xhook_register(lib_ctr, "pthread_exit",reinterpret_cast<void *>(HookThreadExit), nullptr);return true;
}
来到这里,hook实现的地方找到了,还是通过爱奇艺的xhook,把线程操作的系统API给hook出来了。
到这里,整体的实现思路出来了,通过looper不断轮询获取handler定时发送的message去refresh一些进程里面各个线程相关信息。
而线程信息则是通过native hook技术中中PLT hook来实现hook和信息获取。
上述分析,知道了整体实现hook的流程,但是拿到系统API之后,做了什么。下面继续分析:
怎么判断线程是否泄漏
AddThread
void ThreadHolder::AddThread(int tid, pthread_t threadId, bool isThreadDetached,int64_t start_time, ThreadCreateArg *create_arg) {bool valid = threadMap.count(threadId) > 0;if (valid) return;koom::Log::info(holder_tag, "AddThread tid:%d pthread_t:%p", tid, threadId);auto &item = threadMap[threadId];item.Clear();item.thread_internal_id = threadId;item.thread_detached = isThreadDetached;item.startTime = start_time;item.create_time = create_arg->time;item.id = tid;std::string &stack = item.create_call_stack;stack.assign("");try {// native stackint ignoreLines = 0;for (int index = 0; index < koom::Constant::kMaxCallStackDepth; ++index) {uintptr_t p = create_arg->pc[index];if (p == 0) continue;// koom::Log::info(holder_tag, "unwind native callstack #%d pc%p", index,// p);std::string line = koom::CallStack::SymbolizePc(p, index - ignoreLines);if (line.empty()) {ignoreLines++;} else {line.append("\n");stack.append(line);}}// java stackstd::vector<std::string> splits =koom::Util::Split(create_arg->java_stack.str(), '\n');for (const auto &split : splits) {if (split.empty()) continue;std::string line;line.append("#");line.append(split);line.append("\n");stack.append(line);}//空白堆栈,去掉##if (stack.size() == 3) stack.assign("");} catch (const std::bad_alloc &) {stack.assign("error:bad_alloc");}delete create_arg;koom::Log::info(holder_tag, "AddThread finish");
}
这里拿到了线程创建时间和id等信息。
JoinThread
void ThreadHolder::JoinThread(pthread_t threadId) {bool valid = threadMap.count(threadId) > 0;koom::Log::info(holder_tag, "JoinThread tid:%p", threadId);if (valid) {threadMap[threadId].thread_detached = true;} else {leakThreadMap.erase(threadId);}
}
ExitThread
void ThreadHolder::ExitThread(pthread_t threadId, std::string &threadName,long long int time) {bool valid = threadMap.count(threadId) > 0;if (!valid) return;auto &item = threadMap[threadId];koom::Log::info(holder_tag, "ExitThread tid:%p name:%s", threadId,item.name.c_str());item.exitTime = time;item.name.assign(threadName);if (!item.thread_detached) {// 泄露了koom::Log::error(holder_tag,"Exited thread Leak! Not joined or detached!\n tid:%p",threadId);leakThreadMap[threadId] = item;}threadMap.erase(threadId);koom::Log::info(holder_tag, "ExitThread finish");
}
DetachThread
void ThreadHolder::DetachThread(pthread_t threadId) {bool valid = threadMap.count(threadId) > 0;koom::Log::info(holder_tag, "DetachThread tid:%p", threadId);if (valid) {threadMap[threadId].thread_detached = true;} else {leakThreadMap.erase(threadId);}
}
可以发现,代码是通过thread_detached这个参数来判断线程是否泄漏了,假如进程执行了ExitThread,但是thread_detached还没有解除,则判断为线程泄漏。
接着就是收集一些线程的信息,存储到容器里面。
这些为后续做堆栈回溯信息和整体信息的记录做了一些准备。
总结
通过通篇下来,关于线程泄漏监控相关的思路我们是有了,但是具体细节其实还有很多。
篇幅和时间原因,这篇就介绍到这里。
相关文章:
【Android 内存优化】KOOM线程泄漏监控的实现源码分析
文章目录 线程monitor的流程怎么判断线程是否泄漏AddThreadJoinThreadExitThreadDetachThread 总结 前面我们通过研究KOOM的开源代码,研究了关于Java层和native层内存泄漏监控的实现原理。还剩下线程泄漏这部分没有进行分析,今天来补全它。整体下来&…...

【爬虫基础】第1讲 网络爬虫基本知识
什么是网络爬虫 网络爬虫(Web crawler)是一种自动化程序,用于在互联网上收集信息。它可以通过扫描和解析网页的超链接,自动访问网页并抓取所需的数据。网络爬虫常用于搜索引擎和数据采集工具中。 作用 通过有效的爬虫手段批量采…...

scrapy爬虫框架
scrapy爬虫框架 一、scrapy的概念作用和工作流程1、scrapy的概念2、scrapy框架的作用3、scrapy的工作流程(重点)3.1 回顾之前的爬虫流程3.2 改写上述流程3.3 scrapy的流程3.4 scrapy的三个内置对象3.5 scrapy中每个模块的具体作用 二、scrapy的入门使用1…...
【深度学习】基础知识
吴恩达DeepLearning Python # 1.numpy c c.ravel() 将多维数组拉平 # 2.time tic time.time() toc time.time() print(str(1000*(toc- tic))"ms")...
Electron应用自动更新实现及打包部署全攻略
Electron应用自动更新实现及打包部署全攻略 Electron自动更新原理配置更新服务器打包与发布更新全攻略实战步骤部署与测试部署更新测试更新流程错误处理与调试 高级特性与优化用户体验与反馈安全与隐私保护维护与持续集成性能优化结语 在现代跨平台桌面应用开发领域中ÿ…...

【爬虫基础】第6讲 opener的使用
在爬虫中,opener是一个用来发送HTTP请求的对象。它可以用来模拟浏览器发送请求,包括设置请求头、处理Cookie等操作。使用opener可以实现一些高级功能,如模拟登录、处理验证码等。 方法1: from urllib.request import Request,bu…...
Milvus 向量数据库:如何基于docker-compose在本地快速搭建测试环境
文章目录 1. 安装 milvus1.1. milvus v2.3.12版本信息1.2. 安装milvus步骤1.3. 安装管理工具Attu1.4. 将Attu由docker-compose启动参考Milvus 向量数据库专为向量查询与检索设计,能够为万亿级向量数据建立索引,详见介绍请参见: milvus: 专为向量查询与检索设计的向量数据库 …...
python --dejavu音频指纹识别
Dejavu是一个用于音频指纹识别的Python库,它能够将音频文件转换成独特的指纹,然后通过比对数据库中已知音频的指纹,识别出输入音频的来源或相似音频。Dejavu库支持多种类型的音频文件,包括MP3、WAV等,同时也支持在不同…...

完全二叉树的层序遍历[天梯赛]
文章目录 题目描述思路 题目描述 输入样例 8 91 71 2 34 10 15 55 18 输出样例 18 34 55 71 2 10 15 91思路 完全二叉树最后一层可以不满,但上面的每一层的节点数都是满的 后序遍历的顺序为"左右根",我们可以用数组模拟完全二叉树,…...

C语言看完我这篇编译与链接就够啦!!!
1. 前言 Hello!大家好我是小陈,今天来给大家介绍最详细的C语言编译与链接。 2. 编译和链接 我们通常用的编译器,比如Visual Sudio,这样的IDE(集成开发环境)一般将编译和链接的过程一步完成,通常将这这种编译和链接合…...

【React】react 使用 lazy 懒加载模式的组件写法,外面需要套一层 Loading 的提示加载组件
react 组件按需加载问题解决 1 错误信息2 解决方案 1 错误信息 react 项目在创建 router 路由时,使用 lazy 懒加载时,导致以下报错: The above error occurred in the <Route.Provider> component:Uncaught Error: A component suspe…...

IDEA的Scala环境搭建
目录 前言 Scala的概述 Scala环境的搭建 一、配置Windows的JAVA环境 二、配置Windows的Scala环境 编写一个Scala程序 前言 学习Scala最好先掌握Java基础及高级部分知识,文章正文中会提到Scala与Java的联系,简单来讲Scala好比是Java的加强版&#x…...
LeetCode第四天(448. 找到所有数组中消失的数字)
给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。 官解: 方法一:原地修改 思路及解法 我们可以用一个哈希表记录数组 nums 中…...
【vivado】在原有工程上新建工程
一、前言 在工作中,我们经常需要接触到别人的工程,并在别人的工程上新加设计功能,此时我们需要以别人工程为基础新建工程。 二、在已有工程上新建工程的方法 2.1 vivado 页面file-project-save as... 该方法的优点为:可以直接…...

(原型与原型链)前端八股文修炼Day5
一 原型链的理解 原型链定义: 原型链是 JavaScript 中实现对象继承的关键机制之一,它是一种对象之间的关系,通过这种关系,一个对象可以继承另一个对象的属性和方法。 原型链的组成: 每个对象都有一个指向另一个对象的…...

逐步学习Go-并发通道chan(channel)
概述 Go的Routines并发模型是基于CSP,如果你看过七周七并发,那么你应该了解。 什么是CSP? "Communicating Sequential Processes"(CSP)这个词组的含义来自其英文直译以及在计算机科学中的使用环境。 CSP…...

鸿蒙HarmonyOS应用开发之Node-API开发规范
当传入napi_get_cb_info的argv不为nullptr时,argv的长度必须大于等于传入argc声明的大小。 当argv不为nullptr时,napi_get_cb_info会根据argc声明的数量将JS实际传入的参数写入argv。如果argc小于等于实际JS传入参数的数量,该接口仅会将声明…...
单例模式
文章目录 单例模式特殊类的设计单例模式饿汉模式懒汉模式懒汉VS饿汉懒汉的线程安全单例对象的释放 单例模式 认识单例模式之前先认识一下几种常见的特殊类的设计。 特殊类的设计 设计一个类 只能再堆上创建对象 只能再堆上创建,则通过new来创建对象。 将类的构造函…...
Android OpenMAX - 开篇
Android Media是一块非常庞大的内容,上到APP的书写,中到播放器的实现、封装格式的了解,下到OMX IL层的实现、Decoder的封装,每一块都需要我们下很大的功夫学习。除此之外,我们还要对一些相关的模块进行了解,…...
ubuntu开启ssh服务
1.安装openssh-server sudo apt-get install openssh-server 2.开启服务 sudo servive ssh start 3.设置开机自启动 sudo systemctl enable ssh 4.检查是否开启成功 ps -ef | grep ssh 如使用xshell等连接时失败,可以尝试关闭防火墙: sudo sys…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...