内存泄漏
内存泄漏的核心原因是调用分配与释放时没有符合开闭原则。有分配却没有释放,这自然会使得进程的堆内存会越来越少,直到耗尽。
一个内存泄漏检测工具需要能够精准地定位到泄漏是由代码中的哪一行所引起的。
可以直接使用一些现成的工具进行内存泄漏的排查,比如valgrind、mtrace、ASAN等等。但这些工具往往不适用于在生成环境中实时地检测内存泄漏,而多是用于在发现内存泄漏后进行测试。特别是valgrind和ASAN对性能的影响也比较大,而且不适用于嵌入式系统。
那么,是否可以自己写一些小工具,对malloc
和free
做一些简单的跟踪,然后可以随时比较直观地查看是否有内存泄漏呢?
跟踪malloc和free
显然,要检测内存泄漏最基本的思路就是对malloc
和free
的调用情况进行记录,调用malloc
返回一个地址时,对该地址增加一条记录,然后对该地址调用free
时,则撤销该记录。最后程序正常运行结束后,还保留下来的那些记录就是内存泄漏的证据了。
首先想到的就是加hook。那么为什么不能像死锁检测组件那样,用dlsym
直接对malloc
和free
加钩子呢?
大量的函数如printf
等等本身内部存在调用malloc
,在我们重写malloc
时难以避免会使用这些函数,这可能引起递归调用:
malloc()
...
sprintf()
...
malloc()
....
使用宏定义替换
那怎么办呢?可以考虑用宏定义去替换malloc
,比如这样:
void* __malloc(size_t size, const char* file, int line) {
/*...*/
malloc(size);
/*...*/
}
void __free(void* ptr, const char* file, int line) {
/*...*/
free(ptr);
/*...*/
}
#define malloc(size) __malloc(size, __FILE__, __LINE__)
#define free(ptr) __free(ptr, __FILE__, __LINE__)
这样一来在该宏定义之后,调用malloc
和free
时实际就是调用__malloc
和__free
了。并且因为这两个函数都传入了文件名和调用位置,因此可以很方便地查看出是在哪些位置调用了某个malloc。
修改后的测试代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void* __malloc(size_t size,