前言
当动态申请的内存在其管理者(指针)的生命周期结束后,没有被归还给操作系统时,就发生了内存泄露,“泄露”的内存在应用程序退出前无法被访问,且无法被归还。随着程序的持续地运行,“泄露”的内存越多,进程可用的虚拟内存就越少,久而久之,应用程序和操作系统都会变得运行缓慢,更严重将导致程序崩溃。
为了避免这种现象的发生,除了在开发过程中进行正确地小心地管理内存外(事实上即使小心谨慎也难免出错),内存泄露检测工具将为我们提供更加有效的保障。
该系列将探讨几种内存泄露检测的实现方式。这些实现方式的出发点就是对 malloc/free
, new/delete
的调用进行监控,所以理论上,这些方式对内存的重复释放(DUF)、内存的错误释放(BAF) 的检测也行之有效。参考常见的内存错误。
实现一、定义钩子
由于 malloc
和 free
总是成对出现,所以可以通过劫持 malloc/free/realloc
等函数,注入自定义代码用于统计当前函数的调用次数以及该函数的调用位置和调用堆栈信息。内存泄露的统计报告信息将如下:
Memory Leak:
Found memory leak of size 10 at address 0x82345f,
the malloc() of call stack info:
- foo() at line 57 in test.c
- main() at line 82 in test.c
Duplicate Free:
Found duplcate free at address 0x82345f,
the free() of call stack info:
- foo() at line 57 in test.c
- main() at line 82 in test.c
下面将逐步实现:
1. 实现 malloc()
hook
由于许多系统调用内部也存在对 malloc()
的调用,例如 printf()
, 所以在我们定义的malloc hook 函数中使用这些系统调用将导致 malloc()
的无限递归。为了避免这种情况,实现 hook 时必须使用底层接口 __libc_malloc()
(glibc malloc.c
中定义)来申请内存。
此外,由于 hook 只统计当前应用程序的 malloc
调用情况,不应该统计系统调用和第三方库中的接口对 malloc
的调用。所以还需一个全局(静态)变量区分两者。
// Example 1
extern void *__libc_malloc(size_t bytes);
int malloc_count = 0;
void *malloc(</