一、内存泄漏的原因
我们的程序向系统申请分配了一块内存(new/malloc)给对象使用,程序使用完这块内存后没有释放(delete/free),导致这个不使用的对象一直占据内存单元,造成系统将不能再把它分配给需要的程序。
内存泄露简单来说就是,内存分配与内存释放没有做到一对一的匹配。
二、内存泄漏的危害
堆内存被不断的分配使用,没有及时回收,随着程序的运行,堆内存会慢慢的被消耗殆尽,当其他程序需要内存时,系统无法及时的分配合适的内存供其使用,将会造成程序崩溃。
三、如何预防内存泄漏
1、规范动态内存的使用,尽量避免不当用法;
2、及时检测到程序中有内存泄漏,并准确的定位到内存泄漏的具体位置(内存泄漏检测组件);
四、内存泄漏检测组件的实现原理
我们已经知道了内存泄漏的原因,那么内存泄露检测组件的关键就是:
1、内存的申请(new/malloc)与回收(delete/free)是否成对匹配;
将系统的(new/malloc)与(delete/free)hook住,在自己实现的hook函数内部进行申请与回收的匹配计算;
2、定位到准确的内存泄露位置(具体到代码的哪一行)。
1)c语言中有宏定义可以解决此问题:
__LINE__ :当前程序行的行号,表示为十进制整型常量
__FILE__ :当前源文件名,表示字符串型常量
2)__builtin_return_address(),返回当前函数的调用者的地址,通过地址可以得到具体的行号。
五、代码实现内存泄漏组件
编译的时候一定要加上 -g 参数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/*
1、用两个宏hook住系统的malloc/free,宏定义一定要在hook函数(malloc_hook、free_hook)之后,
不然hook函数会一直递归下去(因为执行不到系统的malloc函数),导致段错误;
2、用申请出来的内存地址作为文件名,malloc_hook时,生成文件,将文件名行号等信息放在文件中,
free_hook时,将同名的文件删掉(unlink()),并释放掉内存,剩下的文件就是没有被释放的内存信息;
3、不用__FILE__, __LINE__这两个c语言的宏,也可以用__builtin_return_address()实现,这个函数返回的是当前函数的调用者的地址,
然后用addr2line方法分析出此地址对应的文件及行号,
例:addr2line -f -e memory_leak -a 0x4006f7 (在终端执行),-e 可执行程序,-a 地址;
__FILE__, __LINE__这两个宏应该也是通过这种方法实现的。
*/
void *malloc_hook(size_t size, char* file, int line)
{
void *p = malloc(size);
char buff[128] = {0};
sprintf(buff, "./mem/%p.mem", p);//将申请到的内存地址作为文件名
FILE *fp = fopen(buff, "w");
fprintf(fp, "[+%s:%d]--> addr:%p, size:%ld\n", file, line, p, size);//将申请内存的信息存放在文件中:file文件中的line行,申请的内存地址为p,大小为size
fflush(fp);
fclose(fp);
return p;
}
void free_hook(void *p, char* file, int line)
{
char buff[128] = {0};
sprintf(buff, "./mem/%p.mem", p);
if (unlink(buff) < 0) //文件不存在,说明内存被重复释放了
{
printf("double free: %p\n", p);
return ;
}
free(p);
}
#define malloc(size) malloc_hook(size, __FILE__, __LINE__)
#define free(p) free_hook(p, __FILE__, __LINE__)
int main()
{
void *p1 = malloc(10);
void *p2 = malloc(20);
p2 = p1;
free(p2);
return 0;
}```
有已经比较成熟的内存检测工具,比如mtrace/muntrace,大致用法如下,具体用法网上有很多自行查找
{
mtrace();
需要内存检测的代码段、、、
muntrace();
}
这种方法的分析道的结果也是一些地址信息,还是需要用上述 3 所讲的工具进行分析,得到肉眼可见的调用位置