MFC内存泄露与检测 素颜马尾好姑娘i 2021-06-24 16:10 438阅读 0赞 内存泄露的含义是:拿走了一块“堆”内存块,在某检查点处,发现没有归还这个内存块。如果是: 地址A = malloc(N); 因为没有调用free(地址A),所以内存泄露了。如果是: 地址B = new 类型T; 因为没有调用delete 地址B,所以内存泄露了。如果是:从用户的内存池中取一个内存块,没有调用相应的归还给内存池的操作,也认为是“内存泄露”。从哪里拿了一个东西,要归还到那个地方去。例如:从图书馆L中借了本书,归还给图书馆B,肯定要挨骂的。同理,从图书馆L中借了本小说,却还给图书馆一本杂志,也是要挨骂的。函数\_CrtDumpMemoryLeaks()功能:检查内存泄露并且在VC的输出窗口打印出泄露的内存块信息。 例子1 : #include #include int main() { int* x = new int(); _CrtDumpMemoryLeaks() return 0; } 输出: Detected memory leaks! Dumping objects -> \{61\} normal block at 0x00382650, 4 bytes long. Data: < > 00 00 00 00 非常好,发现了int\* x对应的内存块泄露了 例子2 : template 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() ; } 输出: Detected memory leaks! \{62\} normal block at 0x00382708, 4 bytes long. \{61\} normal block at 0x00382650, 123 bytes long. 非常不好,它把全局变量t也报告了。这是一个严重的误报。 原因是在\_CrtDumpMemoryLeaks()调用时,全局变量t还没有离开生存期呢,所以此时~Test()未调用呢,delete\[\] m\_p还没调用呢。 例子3 : Test<123> t; int main() { _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); int* x = new int(); return 0; } 输出: Detected memory leaks! \{62\} normal block at 0x00382708, 4 bytes long. 非常好,通过\_CrtSetDbgFlag函数,告知Crt库在程序完全退出时,打印一下内存泄露的情况。 这时,全局变量t已经析构了,所以误报没有了。 例子4 : #include<iostream> 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(); return 0; } 输出: Detected memory leaks! c:/sdfdfsdf/sdfdfsdf.cpp(14) : \{61\} normal block at 0x00382650, 4 bytes long. 太酷了!居然在调试的输出窗口中,显示了造成内存泄露的代码位置。 双击一下,还能自动跳到文本编辑器中对应的代码行上。 例子5 : #include<iostream> using namespace std; int main() { _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); std::cout<<"hello world"<<endl; return 0; } 调用堆栈窗口。看到一些函数的调用关系,顺着点点看看,居然发现了int\* x = new int;这一行。 这个62是怎么知道的呢?原来在内存泄露的输出信息里, 如下所示: Detected memory leaks! \{62\} normal block at 0x00382708, 4 bytes long. 大括号中62就是第62次分配内存时,这块内存泄露了。通过\_CrtSetBreakAlloc调用,告知Crt库,在第62次分配内存的调用时,自动暂停程序,让程序员检查函数调用栈。如何保证下次程序运行时, 第62此分配内存的调用就是int\* x = new int;这句造成的呢? 答案是不能。如果程序没有复杂的时序相关的逻辑(多线程),输入的值是一定的,则程序每次运行的行为是一定的。 例子8 : class Init_before_main { public: Init_before_main() { _CrtSetBreakAlloc(62); } }; Init_before_main g_tmp; int main() { int* x = new int; return 0; } 这是对例子7的一点改进,保证在int\* x = new int;调用之前,调\_CrtSetBreakAlloc(62);。 否则,如下的调用顺序可能会漏过了第62次分配调用 int\* x = new int; \_CrtSetBreakAlloc(62); 如何自己实现内存泄露检查工具呢?思路很简单,重载new,delete运算符,使用自己宏替换malloc和free。 所有的分配和释放动作必须经过我过手,我才能加入点私货(统计信息等)。这样就可以检查内存泄露了。 附: 1、对于类似这样的内存泄露提示: Detected memory leaks! Dumping objects -> \{223\} normal block at 0x003CF650, 4 bytes long. Data: < < > E8 F6 3C 00 我们可以利用CRT函数\_CrtSetBreakAlloc(223); 进行定位。 这个函数,就设置了当分配上面223那块内存时,就中断,然后就可以查看调用栈,知道那里出错了。不过,使用这个来判断,就要仔细地分析了,由于内存的分配是动态的,并不能保证每次分配内存的号码是一样的。 2、内存泄露的全局观念 #include "stdafx.h" #include <afx.h> class CMyClassIncString { public: CMyClassIncString() { m_str = _T("hello, word"); m_str2 = _T("memleaking ?"); } protected: private: CString m_str; CString m_str2; }; int _tmain(int argc, _TCHAR* argv[]) { CMyClassIncString* str = new CMyClassIncString; return 0; } 上面的程序导致了下面的结果, Detected memory leaks! Dumping objects -> f:\\dd\\vctools\\vc7libs\\ship\\atlmfc\\src\\mfc\\strcore.cpp(141) : \{543\} normal block at 0x0003FE68, 42 bytes long. Data: < x > 0C 00 E5 78 0C 00 00 00 0C 00 00 00 01 00 00 00 f:\\dd\\vctools\\vc7libs\\ship\\atlmfc\\src\\mfc\\strcore.cpp(141) : \{542\} normal block at 0x0003FE00, 40 bytes long. Data: < x > 0C 00 E5 78 0B 00 00 00 0B 00 00 00 01 00 00 00 \{541\} normal block at 0x0003FDB8, 8 bytes long. Data: < x > 10 FE 03 00 78 FE 03 00 Object dump complete. 原因是包含CString对象的对象没有被释放. 不要光盯着CString,应该盯着使用CString作类成员的对象是否被正常释放. 参考资料: http://msdn.microsoft.com/en-us/library/x98tx3cf.aspx http://vld.codeplex.com/
还没有评论,来说两句吧...