C++大量的手动分配、回收内存是存在风险的,很有可能因为我们的操作不当导致内存泄露的问题。
介绍一种在Debug模式下测试内存泄露的方法。
先在文件的开头加上如下代码,切记顺序不可以改变。
#define _CRTDBG_MAP_ALLOC
#include<crtdbg.h>
#include <stdlib.h>
第一行的宏,是实现一些内存分配函数向Debug模式的映射。
接下来,对new做一个重定义。
#define NEW_WITH_MEMORY_LEAR_CHECKING new(_NORMAL_BLOCK,__FILE__,__LINE__)
#define new NEW_WITH_MEMORY_LEAR_CHECKING
这里的new采用的是VC++对operator new的一个重载,在< vcruntime_new_debug.h>定义。
完成上边的两步后,程序中new和delete回收的过程中便被VC++监视了,在程序退出的地方调用以下函数检测内存泄露。
_CrtDumpMemoryLeaks();
函数将显示当前内存泄露,也就是程序运行到此行代码时的内存泄露,所有未摧毁的对象都会报出内存泄露,因此这个函数尽量放在后面。
举个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRTDBG_MAP_ALLOC
#include<crtdbg.h>
#include <stdlib.h>
#define NEW_WITH_MEMORY_LEAR_CHECKING new(_NORMAL_BLOCK,__FILE__,__LINE__)
#define new NEW_WITH_MEMORY_LEAR_CHECKING
int main(int argc, char* argv) {
auto p = new int[1];
_CrtDumpMemoryLeaks();
system("pause:");
return 0;
}
VC++的编译器cl.exe在delete后会将内存置为0xcdcd防止再次利用,这是程序结束时p进行回收。
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ),这样无论程序何时终止,都会在终止前调用_CrtDumpMemoryLeaks()。
查找泄露技巧 : 使用_CrtSetBreakAlloc(long lBreakAlloc ),函数参数为内存分配的次数。于是让程序自动在泄露处进入断点,可以在_CrtSetDbgFlag后面添加函数_CrtSetBreakAlloc(18); 然后调试程序时,程序自动中断在第18次分配内存的时的crt代码处,然后只要通过查看调用堆栈就可以轻松看到之前的泄露的代码了 。
也可以在某时刻设置检查点,获取当时内存状态的快照,比较不同时刻内存状态的差异。
1.在关键带你对应用程序的内存状态拍快照。CRT库提供一种结构类型_CrtMemState,可以存储内存状态的快照。
_CrtMemState s1, s2, s3;
2.若要在给定点对内存状态拍快照,向_CrtMemCheckpoin函数传递_CrtMemCheckpoint函数传递_CrtMemState结构。该函数用当前内存状态的快照填充此结构。
_CrtMemCheckpoint(&s1);
3.通过向_CrtMemDumpStatistics函数传递_CrtMemState结构,可以在任意点输出当前内存的状态。
_CrtMemDumpStatistics(&s1);
4.确定代码中部分代码发生内存泄露,可以在该部分之前和之后对内存状态拍快照,然后使用_CrtMemDifference比较这两个状态。
_CrtMemCheckpoint(&s1);
_CrtMemCheckpoint(&s2);
if(_CrtMemDifference(&s3, &s1, &s2))
_CrtMemDumpStatistics(&s3);
比较s1和s2的内存状态,生成这两个状态之间差异的结果s3。
举个例子:
#define _CRTDBG_MAP_ALLOC //必须放在#include<crtdbg.h>之前
#include<iostream>
#include<cstring>
#include<crtdbg.h>
#ifdef _DEBUG //重载new
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
using namespace std;
int main(int argc,char** argv) {
//_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
_CrtSetBreakAlloc(142);
_CrtMemState s1, s2, s3;
char* str1=new char[100];
//_CrtMemCheckpoint( &s1 );//记录内存快照
// _CrtMemDumpStatistics( &s1 );//输出
char* str2=(char*)malloc(sizeof(char)*100);
//_CrtMemCheckpoint( &s2 );
// _CrtMemDumpStatistics( &s2 );
strcpy(str1,"asdfggg");
strcpy(str2,"123465");
cout<<int('a')<<endl;
//if ( _CrtMemDifference( &s3, &s1, &s2) )//比较s1和s2,把比较结果输出到s3
// _CrtMemDumpStatistics( &s3 );// dump 差异结果
_CrtDumpMemoryLeaks();
return 0;
}