MFC記憶體洩露與檢測

十日十乞001發表於2017-06-12

記憶體洩露的含義是:拿走了一塊“堆”記憶體塊,在某檢查點處,發現沒有歸還這個記憶體塊。如果是: 地址A = malloc(N); 因為沒有呼叫free(地址A),所以記憶體洩露了。如果是: 地址B = new 型別T; 因為沒有呼叫delete 地址B,所以記憶體洩露了。如果是:從使用者的記憶體池中取一個記憶體塊,沒有呼叫相應的歸還給記憶體池的操作,也認為是“記憶體洩露”。從哪裡拿了一個東西,要歸還到那個地方去。例如:從圖書館L中借了本書,歸還給圖書館B,肯定要捱罵的。同理,從圖書館L中借了本小說,卻還給圖書館一本雜誌,也是要捱罵的。函式_CrtDumpMemoryLeaks()功能:檢查記憶體洩露並且在VC的輸出視窗列印出洩露的記憶體塊資訊。

例子1 :

  1. #include  
  2. #include  
  3. int main()  
  4. {   
  5.        int* x = new int();  
  6.        _CrtDumpMemoryLeaks()  
  7.        return 0;  
  8. }  

輸出:

Detected memory leaks!

Dumping objects -> {61} normal block at 0x00382650, 4 bytes long. Data: < > 00 00 00 00

非常好,發現了int* x對應的記憶體塊洩露了

 

例子2 :

  1. template struct Test  
  2. {   
  3.     Test()  
  4.     {m_p = new char[Size];}  
  5.     ~Test()  
  6.     { delete[] m_p;}  
  7.     char* m_p;  
  8. };  
  9. Test<123> t;  
  10. int main()  
  11. {  
  12.     int* x = new int();  
  13.     _CrtDumpMemoryLeaks() ;  
  14. }  
輸出:

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 :

  1. Test<123> t;  
  2. int main()  
  3. {  
  4.     _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);  
  5.     int* x = new int();  
  6.     return 0;  
  7. }  

輸出:

Detected memory leaks!

{62} normal block at 0x00382708, 4 bytes long.

非常好,通過_CrtSetDbgFlag函式,告知Crt庫在程式完全退出時,列印一下記憶體洩露的情況。

這時,全域性變數t已經析構了,所以誤報沒有了。

 

例子4 :

  1. #include<iostream>  
  2. using namespace std;  
  3. #define DEBUG_NEW new(/*_NORMAL_BLOCK,*/ __FILE__, __LINE__)  
  4. #define new DEBUG_NEW  
  5. int main()  
  6. {  
  7.     _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);  
  8.     int* x = new int();  
  9.     return 0;  
  10. }  

輸出:

Detected memory leaks!

c:/sdfdfsdf/sdfdfsdf.cpp(14) :

{61} normal block at 0x00382650, 4 bytes long.

太酷了!居然在除錯的輸出視窗中,顯示了造成記憶體洩露的程式碼位置。

雙擊一下,還能自動跳到文字編輯器中對應的程式碼行上。

 

例子5 :

 

  1. #include<iostream>   
  2. using namespace std;  
  3. int main()  
  4. {   
  5.     _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);  
  6.     std::cout<<"hello world"<<endl;  
  7.     return 0;  
  8. }  

呼叫堆疊視窗。看到一些函式的呼叫關係,順著點點看看,居然發現了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 :

  1. class Init_before_main  
  2. {  
  3. public: Init_before_main()  
  4.         { _CrtSetBreakAlloc(62);  
  5.         }  
  6. };  
  7. Init_before_main g_tmp;  
  8.   
  9. int main()  
  10. {  
  11.     int* x = new int;  
  12.     return 0;  
  13. }  

這是對例子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、記憶體洩露的全域性觀念

  1. <span style="font-size:14px;">#include "stdafx.h"  
  2. #include <afx.h>  
  3. class CMyClassIncString  
  4. {  
  5. public:  
  6.     CMyClassIncString()  
  7.     {  
  8.         m_str = _T("hello, word");  
  9.         m_str2 = _T("memleaking ?");  
  10.     }  
  11. protected:  
  12. private:  
  13.     CString m_str;  
  14.     CString m_str2;  
  15. };  
  16. int _tmain(int argc, _TCHAR* argv[])  
  17. {  
  18.     CMyClassIncString* str = new CMyClassIncString;   
  19.     return 0;  
  20. }</span>  

上面的程式導致了下面的結果,

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/


相關文章