大型网站得多少钱/凡科网怎么建网站
文章目录
- 线程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…...

测试缺陷定位的基本方法
前后端bug特征 后端: 业务逻辑问题:如任务状态未扭转成功,创建任务失败等数据类问题:如新增的任务在页面没有展示出来等性能类问题:提交任务一直显示创建中、批量操作等待耗时长超时等 前端: 页面显示类…...

【数字图像处理matlab系列】数组索引
【数字图像处理matlab系列】数组索引 【先赞后看养成习惯】【求点赞+关注+收藏】 MATLAB 支持大量功能强大的索引方案,这些索引方案不仅简化了数组操作,而且提高了程序的运行效率。 1. 向量索引 维数为1xN的数组称为行向量。行向量中元素的存取是使用一维索引进行的。因此…...

【2024系统架构设计】案例分析- 3 数据库
目录 一 基础知识 二 真题 一 基础知识 1 ORM ORM(Object—Relationl Mapping...

vue基础——java程序员版(总集)
前言: 这是一个java程序员的vue学习记录。 vue是前端的主流框架,按照如今的就业形式作为后端开发的java程序员也是要有所了解的,下面是本人的vue学习记录,包括vue2的基本使用以及引入element-ui,使用的开发工具…...

Rancher(v2.6.3)——Rancher配置Harbor镜像仓库
Rancher配置Harbor镜像仓库详细说明文档:https://gitee.com/WilliamWangmy/snail-knowledge/blob/master/Rancher/Rancher%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3.md#8rancher%E9%85%8D%E7%BD%AEharbor ps:如果觉得作者写的还行,能够满足您的需…...

C++类和对象、面向对象编程 (OOP)
文章目录 一、封装1.抽象、封装2.类和对象(0)学习视频(1)类的构成(2)三种访问权限(3)struct和class的区别(4)私有的成员变量、共有的成员函数(5)类内可以直接访问私有成员,不需要经过对象 二、继承三、多态1.概念2.多态的满足条件3.多态的使用条件4.多态原理剖析5.纯…...

Introduction to Data Mining 数据挖掘
Why Data Mining? • The Explosive Growth of Data: from terabytes to petabytes — Data collection and data availability ◦ Automated data collection tools, database systems, Web, computerized society — Major sources of abundant data ◦ Business: Web, e-co…...

常用的 Git 命令
初始化一个新的仓库: git init 克隆一个仓库: git clone <仓库地址> 查看文件状态: git status 添加文件到暂存区: git add <文件名> 提交文件到仓库: git commit -m "提交说明" 查看提交历…...

jackson:JSON字符串(String)类型的成员序列化和反序列化
对于如下类型定义TestTaskInfo,props字段为JSON字符串(这在数据库经常用到),可以自由保存各种类型的数据 Data public class TestTaskInfo {private String id;private String props;public TestTaskInfo() {}public TestTaskInfo(String id, String props) {super…...

使用docker的好处???(docker的优势)
标准化环境: Docker通过容器技术封装应用程序及其依赖(如库、配置文件、运行时环境等),确保应用程序在任何环境中都能以一致的方式运行。这种标准化消除了“在我机器上能运行”的问题,因为容器化应用能在开发、测试、生…...