我撸了个内存泄漏检测工具,只用了两招

本文转载自微信公众号「程序喵大人」,撸个两招作者程序喵大人。内存转载本文请联系程序喵大人公众号。泄漏
大家看我写了这么长时间C++文章,检测殊不知我在工作中已经一年多没有用过C++了,工具最近做一个新项目,只用终于又回到C++的撸个两招怀抱了,有点激动,内存也有点不适应。泄漏
不管使用什么语言,检测一定要处理好内存问题,工具要有检测内存问题的只用方法论,于是撸个两招撸了个检测是否有泄漏的小工具,这里分享一波。内存
先贴个效果图:

实现方法
众所周知C++中申请和释放内存使用的泄漏是new和delete关键字:
void func() { A* a = new A(); delete a; A* b = new int[4]; delete[] b; }再明确下需求:如果程序中存在内存泄漏,我们的目的是找到这些内存是在哪里分配的,如果能够具体对应到代码中哪一个文件的那一行代码最好。好了需求明确了,开始实现。
内存在哪里释放的我们没必要监测,云南idc服务商只需要检测出内存是在哪里申请的即可,如何检测呢?
整体思路很简单:在申请内存时记录下该内存的地址和在代码中申请内存的位置,在内存销毁时删除该地址对应的记录,程序最后统计下还有哪条记录没有被删除,如果还有没被删除的记录就代表有内存泄漏。
很多人应该都知道new关键字更底层是通过operator new来申请内存的:
void* operator new(std::size_t sz)也就是正常情况下C++都是通过operator new(std::size_t sz)来申请内存,而这个操作符我们可以重载:
void* operator new(std::size_t size, const char* file, int line); void* operator new[](std::size_t size, const char* file, int line);tip:new和new[]的区别我就不具体介绍了,太基础。
如果能让程序申请内存时调用重载的这个函数,就可以记录下内存申请的具体位置啦。
怎么能够让底层程序申请内存时调用重载的这个函数呢?这里可以对new使用宏定义:
#define new new (__FILE__, __LINE__)有了这个宏定义后,在new A的时候底层就会自动调用operator new(std::size_t size, const char* file, int line)函数,至此达到了我们记录内存申请位置的目的。
这里有两个问题:
在哪里记录内存申请的位置等信息呢?如果在operator new内部又申请了一块内存,用于记录位置,云服务器提供商那新申请的这块内存需要记录不?这岂不是递归调用了? 只有在new宏定义包裹范围内申请了内存才会被记录,然而某些第三方库或者某些地方没有被new宏定义包裹,可能就无法被监测是否申请了内存吧?下面逐个击破:
哪里存储具体信息?
我们肯定不能让它递归调用啊,那这些信息存储在哪里呢?这里可以在每次申请内存时,一次性申请一块稍微大点的内存,具体信息存储在多余的那块内存里,像这样:
static void* alloc_mem(std::size_t size, const char* file, int line, bool is_array) { assert(line >= 0); std::size_t s = size + ALIGNED_LIST_ITEM_SIZE; new_ptr_list_t* ptr = (new_ptr_list_t*)malloc(s); if (ptr == nullptr) { std::unique_lock<std::mutex> lock(new_output_lock); printf("Out of memory when allocating %lu bytesn", (unsigned long)size); abort(); } void* usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; if (line) { strncpy(ptr->file, file, _DEBUG_NEW_FILENAME_LEN - 1)[_DEBUG_NEW_FILENAME_LEN - 1] =