学习笔记:VS2005内存泄露检查
内存泄露指在某检查点,拿走的“堆”内存块未被归还。比如:
- 使用
malloc
分配内存,如地址A = malloc(N);
,若未调用free(地址A)
,就会造成内存泄露。 - 使用
new
分配内存,像地址B = new 类型T;
,若未调用delete 地址B
,也会出现内存泄露。 - 从用户内存池中获取内存块后,未执行相应的归还操作,同样属于“内存泄露”。
_CrtDumpMemoryLeaks()
函数的功能是检查内存泄露,并在VC的输出窗口打印出泄露的内存块信息。
下面通过多个例子详细介绍在VS2005中检查内存泄露的方法:
- 示例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输出:
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输出:
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输出:
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. - 示例5输出:
1
2
3
4
5#include <iostream>
int main() {
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
std::cout << "hello world" << std::endl;
}使用STLPort时出现了一大块内存泄露,换成微软自带的STL则正常。推测是1
2Detected memory leaks!
{62} normal block at 0x00386028, 4480 bytes long.cout
这个全局变量中有一大块预分配的IO缓冲区未释放,该误报尚未解决。 - 示例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;
}该API不仅能查找程序开始执行时的内存泄露,还能在任意一段代码段之间查找内存泄露。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. - 示例7输出:无。启动调试(F5),调试器会在
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 |
|
这是对示例7的改进,确保在int* x = new int;
调用之前调用_CrtSetBreakAlloc(62);
,避免出现int* x = new int;
在前,_CrtSetBreakAlloc(62);
在后而漏过第62次分配调用的情况。
自己实现内存泄露检查工具的思路:重载new
和delete
运算符,使用自己的宏替换malloc
和free
。让所有的分配和释放动作都经过处理,以便加入统计信息等,从而实现内存泄露检查。
学习笔记:VS2005内存泄露检查
https://jycpp.github.io/11-08-11-学习笔记VS2005内存泄露检查.html