前言:
各位朋友们国庆快乐,恭喜大家过完今年所有的假期!!!
这篇文字是来源于某个小家伙的提问,我寻思这个月也没啥值得记录的,就随便写写,说不定一些老家伙也不知道如何在短时间内快速分析三方程序的内存泄露问题呢
这里我们只涉及常规的linux和windows,有其他特殊要求的朋友请绕道;同时我们假设有调试符号的存在,如果你要分析的文件既没有调试符号,又经过了混淆编译,那只能祝你在反汇编的海洋中自由翱翔了。
前提条件:
首先,我们需要借助一些常见的系统工具来获取监控信息。而这类三方软件可以说是比比皆是,我们选取的逻辑是:尽量贴近系统、尽量简单。
所以,我们要求,在windows下面,需要对应的windows调试工具,即Windows Kits;在linux下面,我们以ubuntu为例,需要Valgrind。
至于vtune和mtrace这类的东西,使用场景受限,不推荐用来快速定位问题。
使用范围:
主要情况为你已经明确知道了触发内存泄露的步骤(本文不包含静态检查的内容)的情况下,希望对三方库、开源项目、同事丢来的屎山进行问题定位的情况。
正文步骤:
内存泄露对于c++ er来说就和吃饭喝水一般自然,谁都有拿了东西忘记放回原位的情况,定时重启就能修复百分之九十的问题。但是,如果漏的过于凶猛,就可能会影响到我们在客户眼中的形象了。
泄露的原理大家都知道,分配了、申请了,然后迟迟不还给系统。类似于借钱不还,所以,我们只需要知道两个部分,谁借的,借了多少。对应过来就是分配堆栈、分配次数与大小。
所以,我们需要有一个trace stack和一个监控malloc的工具。
首先,我们模拟一下内存泄露的情况:
#include <iostream>
#include <new> // For std::bad_alloc
#include <thread> // For std::this_thread::sleep_for
#include <chrono> // For std::chrono::seconds
int main() {
const int memory_leak_size = 500 * 1024; // 500KB
const int leak_rate = 1; // Leak every 1 second
while (true) {
try {
// Allocate memory without freeing it
char* leak = new char[memory_leak_size];
// Simulate some work
std::this_thread::sleep_for(std::chrono::seconds(leak_rate));
} catch (const std::bad_alloc& e) {
// Handle memory allocation failure
std::cerr << "Memory allocation failed: " << e.what() << std::endl;
break;
}
}
return 0;
}
windows的内存泄露分析:
windows下面我们还是使用微软全家桶,主要用的工具是两个:gflags和umdh。
首先,我们需要在gflags对对应的进程添加标志位
在系统变量中加上对应进程的变量
运行进程,获取内存快照文件
使用umdh对快照文件进行比对分析:
现在,我们看看分析结果:
可以看到,这里显示两次快照之间总共泄露了14次内存,总泄露的量为7001KB,换算过来一次泄露500KB,与我们的测试代码吻合。
同时,我们也可以根据这里的调用堆栈查询到对应的代码行数,根据反汇编工具,就可以对问题进行盖棺定论了。
linux下的内存泄露分析:
linux下面原理是一样的,需要安装valgrind
编译完可执行文件之后,我们直接使用命令启动valgrind中的内存检查组件:
程序停止运行之后,检查输出的文件
相比于windows下面,这里的检查更加的智能一些,更像是分析之后的结果,我们看到这里显示泄露15次,总计7500KB,同样和我们的程序一致。
后续的分析定位方式和windows一样。
注意事项:
开启gflags或者valgrind之后,程序的运行会被严重减慢,所以,在使用完成之后,需要将对应的标志位清零
同时,gflags和valgrind的功能远不止如此,我们在后续的文章中会提到他们的其他功能。
Q.E.D.