C/C++的内存泄漏检测工具Valgrind memcheck的使用经历(求大神解答疑惑,找出内存泄露真凶)(一)

2014-11-24 10:54:32 · 作者: · 浏览: 4
1. 最多最低级的错误:不匹配地使用malloc/new/new[] 和 free/delete/delete[]
这样的错误主要源于我对C++的new/new[]、delete/delete[]机制不熟悉,凡是new/new[]分配内存的类型变量我一概用delete进行释放,或者有的变量用malloc进行分配,结果释放的时候却用delete,导致申请、释放很多地方不匹配,很多内存空间没能释放掉。为了维护方便,我后来一律使用new/new[]和delete/delete[],抛弃C中的malloc和free。
如果将用户new的类型分为基本数据类型和自定义数据类型两种,那么对于下面的操作相信大家都很熟悉,也没有任何问题。
(1)基本数据类型
一维指针:
// 申请空间
int *d = new int[5];
// 释放空间
delete[] d;
二维指针:
复制代码
// 申请空间
int **d = new int*[5];
for (int i = 0; i < 5; i++)
d[i] = new int[10];
// 释放空间
for (int i = 0; i < 5; i++)
delete[] d[i];
delete[] d;
复制代码
(2)自定义数据类型
比如下面这样一个类型:
复制代码
class DFA {
bool is_mark;
char *s;
public:
~DFA() { printf("delete it.\n"); }
};
复制代码
一维指针:
DFA *d = new DFA();
delete d;
二维指针:
复制代码
// 申请空间
DFA **d = new DFA*[5];
for (int i = 0; i < 5; i++)
d[i] = new DFA();
// 释放空间
for (int i = 0; i < 5; i++)
delete d[i];
delete[]d;
复制代码
这没有任何问题,因为我们都是配套使用new/delete和new[]/delete[]的。这在Valgrind下检测也是完美通过的,但为什么要这配套使用呢?原理是什么?
虽然深究这些东西好像没什么实际意义,但对于想深入了解C++内部机制或像我一样老是释放出错导致大量内存泄露的小白程序员还是值得研究的,至少知道了为什么,以后就不会犯现在的低级错误。
参考文献(3)是这样描述的:
通常状况下,编译器在new的时候会返回用户申请的内存空间大小,但是实际上,编译器会分配更大的空间,目的就是在delete的时候能够准确的释放这段空间。
这段空间在用户取得的指针之前以及用户空间末尾之后存放。
实际上:blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize; 其中,blockSize 是系统所分配的实际空间大小,_CrtMemBlockHeader是new的头部信息,其中包含用户申请的空间大小等其他一些信息。 nNoMansLandSize是尾部的越界校验大小,一般是4个字节“FEFEFEFE”,如果用户越界写入这段空间,则校验的时候会assert。nSize才是为我们分配的真正可用的内存空间。
用户new的时候分为两种情况
A. new的是基础数据类型或者是没有自定义析构函数的结构
B. new的是有自定义析构函数的结构体或类
这两者的区别是如果有用户自定义的析构函数,则delete的时候必须要调用析构函数,那么编译器delete时如何知道要调用多少个对象的析构函数呢,答案就是new的时候,如果是情况B,则编译器会在new头部之后,用户获得的指针之前多分配4个字节的空间用来记录new的时候的数组大小,这样delete的时候就可以取到个数并正确的调用。
这段描述可能有些晦涩难懂,参考文献(4)给了更加详细的解释,一点即通。这样的解释其实也隐含着一个推论:如果new的是基本数据类型或者是没有自定义析构函数的结构,那么这种情况下编译器不会在用户获得的指针之前多分配4个字节,因为这时候delete时不用调用析构函数,也就是不用知道数组个数的大小(因为只有调用析构函数时才需要知道要调用多少个析构函数,也就是数组的大小),而是直接传入数组的起始地址从而释放掉这块内存空间,此时delete与delete[]是等价的。
因此下面的释放操作也是正确的:
// 申请空间
int *d = new int[5];
// 释放空间
delete d;
将其放在Valgrind下进行检测,结果如下:
复制代码
==2955== Memcheck, a memory error detector
==2955== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2955== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2955== Command: ./test_int
==2955==
==2955== Mismatched free() / delete / delete []
==2955== at 0x402ACFC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==2955== by 0x8048530: main (in /home/hadoop/test/test_int)
==2955== Address 0x434a028 is 0 bytes inside a block of size 20 alloc'd
==2955== at 0x402B774: operator new[](unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==2955== by 0x8048520: main (in /home/hadoop/test/test_int)
==2955==
==2955==
==2955== HEAP SUMMARY:
==2955== in use at exit: 0 bytes in 0 blocks
==2955== total heap usage: 1 allocs, 1 frees, 20 bytes allocated
==2955==
==2955== All heap blocks were freed -- no leaks are possible
==2955==
==2955== For counts of detected and suppressed errors, rerun with: -v
==2955== E