首先必须搞懂什么才是Memory Leak?其实业界一直有两种定义。
a. 大众化说法:一块内存由new或者malloc分配了,在程序结束前一直没有被回收。但程序始终有指针指向这块内存。
b. 更严格的说法:一块内存由new或者malloc分配在堆上面,在程序结束前,已经没有任何指针能够指向这块内存了。
对于第一种Memory Leak,其实危害不大,因为一旦进程结束了,所有该进程分配的内存(包括Leak掉的内存)都会被kernel回收。对于第二种Memory Leak,大家一致认为对大型服务器端运行的程序是有危害的。如果程序一直在执行,Leak的内存会越来越多,可能会耗尽程序的堆内存。
相应的,在Valgrind里面,称第一种Leak为“still reachable”,称第二种为“memory leak”。对于memory leak又分为两种。Directly lost和Indirectly lost。
上图中的BBB在case (1),(2)情况下是Still reachable。Case (3)是Directly lost,而Case(4)是Indirectly lost。用一个例子能够很清楚的说明问题。
#include #include <string.h> void **rrr; int main(void) { /* Allocation of AAA block, start-pointer in RRR */ rrr = malloc(sizeof(void *)); /* Allocation of BBB block, start-pointer in AAA */ *rrr = strdup("bbb"); /* oops, we lost the start-pointer to AAA */ rrr = NULL; return 0; }
用Valgrind检查上面的代码:
$ valgrind --leak-check=full --show-reachable=yes ./a.out ==16420== 4 bytes in 1 blocks are indirectly lost in loss record 1 of 2 ==16420== at 0x4A05E1C: malloc (vg_replace_malloc.c:195) ==16420== by 0x374EA79771: strdup (in /lib64/libc-2.5.so) ==16420== by 0x4005E2: main (in /home/zzhan17/test/a.out) ==16420== ==16420== 12 (8 direct, 4 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2 ==16420== at 0x4A05E1C: malloc (vg_replace_malloc.c:195) ==16420== by 0x4005CA: main (in /home/zzhan17/test/a.out) ==16420== ==16420== LEAK SUMMARY: ==16420== definitely lost: 8 bytes in 1 blocks ==16420== indirectly lost: 4 bytes in 1 blocks ==16420== possibly lost: 0 bytes in 0 blocks ==16420== still reachable: 0 bytes in 0 blocks ==16420== suppressed: 0 bytes in 0 blocks
如果把上面的“rrr = NULL; ”代码删除, Valgrind不会报告任何memory leak但会显示有memory still reachable,原因是void **rrr是global pointer,程序退出时rrr还能指向AAA和BBB。
因此,最为重要的是分析Definitely lost和Indirectly lost,也就是以上的Case 3, 4和9。所谓的Interior lost是指向block内的指针,也就是我们常说的野指针。所以很多Possible lost并不是真正的Memory leak。
以后有空的话还会给大家介绍Valgrind的其他模块,如Callgrind,CacheGrind,还有图形化的工具KCacheGrind。这些对Performance Tuning都很有帮助。