学习笔记:VS2005内存泄露检查
内存泄露指在某检查点,拿走的“堆”内存块未被归还。比如: -
使用malloc分配内存,如地址A = malloc(N);,若未调用free(地址A),就会造成内存泄露。
-
使用new分配内存,像地址B = new 类型T;,若未调用delete 地址B,也会出现内存泄露。
-
从用户内存池中获取内存块后,未执行相应的归还操作,同样属于“内存泄露”。
_CrtDumpMemoryLeaks()函数的功能是检查内存泄露,并在VC的输出窗口打印出泄露的内存块信息。
下面通过多个例子详细介绍在VS2005中检查内存泄露的方法: 1.
示例1 1
2
3
4
5
6
7#include <stdlib.h>
#include <crtdbg.h>
int main() {
int* x = new int();
_CrtDumpMemoryLeaks();
}1
2
3
4Detected memory leaks!
Dumping objects ->
{61} normal block at 0x00382650, 4 bytes long.
Data: < > 00 00 00 00int* x对应的内存块泄露。 2.
示例2 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16template <int Size>
struct Test {
Test() {
m_p = new char[Size];
}
~Test() {
delete[] m_p;
}
char* m_p;
};
Test<123> t;
int main() {
int* x = new int();
_CrtDumpMemoryLeaks();
}1
2
3Detected memory leaks!
{62} normal block at 0x00382708, 4 bytes long.
{61} normal block at 0x00382650, 123 bytes long._CrtDumpMemoryLeaks()时,全局变量t还未离开生存期,其析构函数~Test()未被调用,delete[] m_p也未执行。
3. 示例3 1
2
3
4
5Test<123> t;
int main() {
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
int* x = new int();
}1
2Detected memory leaks!
{62} normal block at 0x00382708, 4 bytes long._CrtSetDbgFlag函数,让Crt库在程序完全退出时打印内存泄露情况。此时全局变量t已析构,误报消除。
4. 示例4 1
2
3
4
5
6
7
8
9
10#include <string>
using namespace std;
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
#define new DEBUG_NEW
int main() {
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
int* x = new int();
}1
2Detected memory leaks!
c:\sdfdfsdf\sdfdfsdf.cpp(14) : {61} normal block at 0x00382650, 4 bytes long.1
2
3
4
5#include <iostream>
int main() {
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
std::cout << "hello world" << std::endl;
}1
2Detected memory leaks!
{62} normal block at 0x00386028, 4480 bytes long.cout这个全局变量中有一大块预分配的IO缓冲区未释放,该误报尚未解决。
6. 示例6 1
2
3
4
5
6
7
8
9int main() {
_CrtMemState s1, s2, s3;
_CrtMemCheckpoint( &s1 );
new int;
new char;
_CrtMemCheckpoint( &s2 );
if ( _CrtMemDifference( &s3, &s1, &s2) ) _CrtMemDumpStatistics( &s3 );
return 0;
}1
2
3
4
5
6
70 bytes in 0 Free Blocks.
5 bytes in 2 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 0 bytes.
Total allocations: 5 bytes.1
2
3
4
5int main() {
_CrtSetBreakAlloc(62);
int* x = new int;
return 0;
}int* x = new int;语句处暂停。打开“窗口->调用堆栈窗口”,能看到函数调用关系,并找到int* x = new int;这一行。
内存泄露输出信息中的{62}表示第62次分配内存时,这块内存泄露了。通过_CrtSetBreakAlloc调用,可让Crt库在第62次分配内存的调用时自动暂停程序,方便程序员检查函数调用栈。但无法保证下次程序运行时,第62次分配内存的调用就是int* x = new int;这句造成的。若程序没有复杂的时序相关逻辑(多线程)且输入值固定,则程序每次运行的行为是确定的。
8. 示例8 1
2
3
4
5
6
7
8
9
10
11
12class Init_before_main {
public:
Init_before_main() {
_CrtSetBreakAlloc(62);
}
};
Init_before_main g_tmp;
int main() {
int* x = new int;
return 0;
}int* x = new int;调用之前调用_CrtSetBreakAlloc(62);,避免出现int* x = new int;在前,_CrtSetBreakAlloc(62);在后而漏过第62次分配调用的情况。
自己实现内存泄露检查工具的思路:重载new和delete运算符,使用自己的宏替换malloc和free。让所有的分配和释放动作都经过处理,以便加入统计信息等,从而实现内存泄露检查。