当前位置: 首页 > 工具软件 > MemLeak > 使用案例 >

MemLeak学习笔记

邢修明
2023-12-01
今天在手机上测试一个程序的时候无意间发现了一个kernel进程kmemleak,于是就认识了memleak。内存泄露是一个很严重的问题,特别是移动平台上面的开发,如果内存泄露不严重那么查起来的难度将会加大,有了这个小tool对我们的开发或多或少有些帮助。

关于MemLeak官方她自己已经描述得很清楚了,MemLeak -- a module to debug memory leaks in C code and other problems with malloc()--free() invocations. Written in ANSI C.

下载地址:

http://sourceforge.net/projects/memleak/

头文件解析)
memleak.h中提供的方法都是以宏的形式提供出来,她还的头文件还考虑到了有些平台或许没有string.h这个头文件,看来她是考虑到了兼容问题。我主要在Android平台使用她,因为Android平台存在string.h这个头文件,所以编译的时候需要定义WITH_DBG_STRDUP来支持strdup和strndup这两个接口。
用于调试的接口有dbg_init、dbg_check_addr、dbg_mem_stat、dbg_zero_stat、dbg_abort、dbg_heap_dump、dbg_history_dump和dbg_catch_sigsegv。重新定义了malloc、realloc、calloc、free、strdup和strndup,所以当我们调用这几个内存操作接口时都是操作memleak重新定义的这几个接口, 这几个接口之所以重新定义是因为要记录申请和释放的次数及大小。
// for stdlib.h
#define FILE_LINE dbg_file_name = __FILE__, dbg_line_number = __LINE__
#define malloc(s) (FILE_LINE, dbg_malloc(s))
#define realloc(p, s) (FILE_LINE, dbg_realloc(p, s))
#define calloc(n, s) (FILE_LINE, dbg_calloc(n, s))
#define free(p) (FILE_LINE, dbg_free(p))
 
#define dbg_init(n) (FILE_LINE, dbg_init(n))
#define dbg_check_addr(m, p, o) (FILE_LINE, dbg_check_addr(m, p, o))
#define dbg_mem_stat() (FILE_LINE, dbg_mem_stat())
#define dbg_zero_stat() (FILE_LINE, dbg_zero_stat())
#define dbg_abort(m) (FILE_LINE, dbg_abort(m))
#define dbg_heap_dump(k) (FILE_LINE, dbg_heap_dump(k))
#define dbg_history_dump(k) (FILE_LINE, dbg_history_dump(k))
#define dbg_catch_sigsegv() (FILE_LINE, dbg_catch_sigsegv())
 
// for string.h
#define strdup(s) (FILE_LINE, dbg_strdup(s))
#define strndup(s, n) (FILE_LINE, dbg_strndup(s, n))


数据结构)
用于保存调用malloc和calloc的头信息。realloc会得到一个新的大小,也有可能是free掉之前malloc的大小(realloc的大小为0时相当于free),memleak的处理非常好,当realloc申请的大小和之前相同时不做任何处理,当为0时,把这个buff节点从链表中删除,当为其他新的大小时替换之前的链表。
struct head
{
  void *addr;
  size_t size;
  char *file;
  unsigned long line;
  /* two addresses took the same space as an address and an integer on many archs => usable */
  union {
    struct { struct head *prev, *next; } list;
    struct { char *file; unsigned long line; } free;
  } in;
};
这是一个双向链表,所以规定了大小后会只保留最新的数据,相应的操作有add 、del 和replace,具体可以查看源码。


重要函数解析)
【dbg_init】初始化链表大小,用于记录 申请未释放的位置及大小等信息,只保留最新的 dbg_init时的 大小。
【dbg_check_addr】检查内存的状态,有两种状态(CHK_ALLOC和CHK_FREED)
【dbg_catch_sigsegv】注册一个(SIGSEGV)非法内存访问的signal,当遇到非法内存方法访问时先把之前记录的内存申请和释放的次数和大小打印出来,还把刚刚存于链表中的申请信息打印出来,然后再crash。
【dbg_mem_stat】打印调用malloc、realloc、calloc和free的个数,以及未释放的内存大小。
【dbg_zero_stat】所以计数全部清零。
【dbg_abort】主动去结束进程,并把进程abort之前的内存情况打印出来。
【dbg_heap_dump】如果存在内存异常情况,那么这些信息会保留在链表中,这个函数就是把链表的信息打印出来。
【dbg_history_dump】把free的信息打印出来。



#-------------------------------------------------------------------#
初识MemLeak的时候并不觉得有啥,因为通过她的描述大概能知道她的架构以及实现方式。但是当写完笔记之后我却有了新的认识,因为她要比我想得更多、更好、更严谨。
因为平时写C++比较多,很多都是使用new来分配,如果我把new和delele中使用malloc和free等方法替换成MemLeak的malloc和free,就能快速把这个小工具使用在之前的项目中了,因为如果是分配非结构体或者类的内存时最终都是调用malloc来实现内存分配的。关于new和delete的非编译器代码在ndk中可以找到,替换也是很easy的事。但是以后的代码中非必要情况还是使用malloc和free来对内存进行申请和释放。

 类似资料: