全部博文(149)
分类: C/C++
2011-03-11 09:29:11
内存泄露的主要原因在于随着代码量的增大,编码者不能完全跟踪内存分配的状况,当前的编译器对此又没有很好的解决办法,况且动态内存分配是在程序运行中根据具体情况生成的,具有不确定性。
本案例将在程序的调试阶段,通过链表的方式对内存的分配和释放进行实时记录和跟踪,达到检测和避免内存泄露的目的。
欲达到这个目的,需要解决三个问题:链表的实现,malloc的封装,free的封装。
链表,要构造一个适合C与C++使用的链表,可以方便的插入,检索和移除链表节点。本案例将采用contiki操作系统中的单向链表结构。
Malloc通过宏的方式进行封装,实现分配指针的检测,和分配信息录入链表的工作。
Free通过宏的方式进行封装,实现分配空间的释放,和分配信息的移除工作。
程序的最后,要提供信息打印,将泄露内存打印出来。Contiki是一款非常优秀的嵌入式操作系统,其优势之一就是程序的模块化很强,可以根据个人需求,从中拾取相应的模块使用,而代码几乎不需要修改。本案例中用到的单向链表便是一个例子。需要使用contiki的链表实现,只要从其内核中拿出list.c和list.h两个文件,只需要进行必要的路径修改。其他的不需要修改。
其使用方法非常简单,在使用时,通过LIST宏的方式,生成自己需要使用的链表。定义自己的链表节点结构体。定义节点结构体需要注意的是,结构体的第一个元素必须是指向该结构体实例下一个实例的指针,命名为next。该处巧妙的利用了内存重合的优点,也是contiki设计比较经典的地方之一。
该案例中定义的结构体如下:
struct Tmemleak{
struct Tmemleak *next; // 与list 定义保持一致
void *pMalloc;
char achFilename[64];
int nLine;
int nSize;
};
分别记录了分配内存的地址,内存分配的文件名,行号和分配内存的大小。程序启动后,需要对链表进行简单的初始化即可。以后便可以容易的进行链表操作。
Malloc通过宏的方式进行重构,使其具有内存指针检测和分配信息录入的功能。首先进行欲存储分配空间指针的检测,如果其值不为NULL,严谨的讲,其指向一有效内存空间,还未释放,不能对其委以重任。
检测通过后,向其分配有效空间。并生成链表元素,记录该分配动作,将其加入链表。
Free通过宏的方式进行重构,首先检测其是否有效,有效则释放。释放后从链表中移除相应的记录信息,并将记录信息所用内存空间收回。最后,要将指针指向NULL,避免不必要的错误。
在程序的最后,对链表进行遍历,将内存泄露信息打印出来。并将分配内存进行释放。
分配数组为释放的程序打印示例如下:
该结果表示main.c文件中的第9行,分配了50个字节的空间,没有释放。
现有程序想使用该模块进行测试的方法如下:
在Makefile中进行MEM_DEBUG的宏定义,以开启内存调试模式。并将list.c和memleak.c加入编译范围中。
在需要该调试的源文件中,引用头文件memleak.h。在主函数main()的开始处,调用memleak_init()函数进行初始化。
在main()函数的结尾调用memleak_end()输出结果。
通过全文替换的功能,将free替换成FREE。
查找malloc关键词,将语句x = malloc(y),修改成MALLOC(x, y)。
如果要进行版本发布,关闭内存调试功能,只要将Makefile中的-DMEM_DEBUG注释掉即可。