vs中檢測記憶體洩漏的方法

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

使用vs的記憶體檢測有以下幾種方法。

在debug模式下以F5執行:

方法一:

[html] view plain copy
  1. #define CRTDBG_MAP_ALLOC    
  2. #include <stdlib.h>    
  3. #include <crtdbg.h>    
  4. //在入口函式中包含 _CrtDumpMemoryLeaks();    
  5. //即可檢測到記憶體洩露  
  6.   
  7. //以如下測試函式為例:  
  8. int main()  
  9. {  
  10.     char* pChars = new char[10];  
  11.     _CrtDumpMemoryLeaks();  
  12.     return 0;  
  13. }  

F5執行輸出視窗會得到:

Detected memory leaks!
Dumping objects ->
{58} normal block at 0x00341A38, 10 bytes long.
 Data: <          > CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

以上方法沒有輸出

注意:
1.在VS2010下測試的時候,發現_CrtDumpMemoryLeaks();這句必須放在函式結束處,放在主函式入口處輸出視窗不會輸出記憶體洩露資訊
2.{}中的數字指明這塊記憶體是程式中總計第幾個被申請的,這種方法沒有行號和其他資訊輸出。我們可以定義:

[html] view plain copy
  1. #ifdef _DEBUG  
  2. #define New   new(_NORMAL_BLOCK, __FILE__, __LINE__)  
  3. #endif  
  4.   
  5. #define CRTDBG_MAP_ALLOC    
  6. #include <stdlib.h>    
  7. #include <crtdbg.h>    
  8. //在入口函式中包含 _CrtDumpMemoryLeaks();    
  9. //即可檢測到記憶體洩露  
  10.   
  11. //以如下測試函式為例:  
  12. int main()  
  13. {  
  14.     char* pChars = New char[10];  
  15.     _CrtDumpMemoryLeaks();  
  16.     return 0;  
  17. }  

輸出:

Detected memory leaks!
Dumping objects ->
e:\vs2005\stltest\stltest\test.cpp(14) : {58} normal block at 0x00591A38, 10 bytes long.
 Data: <          > CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

方法二:

[html] view plain copy
  1. #define CRTDBG_MAP_ALLOC    
  2. #include <stdlib.h>    
  3. #include <crtdbg.h>    
  4. //在入口函式中包含 _CrtDumpMemoryLeaks();    
  5. //即可檢測到記憶體洩露  
  6.   
  7. //定義函式:  
  8. inline void EnableMemLeakCheck()  
  9. {  
  10.     _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);  
  11. }  
  12. //該函式可以放在主函式的任意位置,都能正確的觸發記憶體洩露輸出  
  13.   
  14.   
  15. //以如下測試函式為例:  
  16. int main()  
  17. {  
  18.     EnableMemLeakCheck();  
  19.     char* pChars = new char[10];  
  20.     //_CrtDumpMemoryLeaks();  
  21.     return 0;  
  22. }  

輸出:

Detected memory leaks!
Dumping objects ->
{58} normal block at 0x004F1A38, 10 bytes long.
 Data: <          > CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

方法三:直接定位指定記憶體塊錯誤的程式碼行

單確定了記憶體洩漏發生在哪一行,有時候並不足夠。特別是同一個new對應有多處釋放的情形。在實際的工程中,以下兩種情況很典型:

建立物件的地方是一個類工廠(ClassFactory)模式。很多甚至全部類例項由同一個new建立。對於此,定位到了new出物件的所在行基本沒有多大幫助。
 
COM物件。我們知道COM物件採用Reference Count維護生命週期。也就是說,物件new的地方只有一個,但是Release的地方很多,你要一個個排除。
那麼,有什麼好辦法,可以迅速定位記憶體洩漏?

答:有。

在記憶體洩漏情況複雜的時候,你可以用以下方法定位記憶體洩漏。這是我個人認為通用的記憶體洩漏追蹤方法中最有效的手段。

我們再回頭看看crtdbg生成的記憶體洩漏報告:

Detected memory leaks!
Dumping objects ->
{58} normal block at 0x004F1A38, 10 bytes long.
 Data: <          > CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

除了產生該記憶體洩漏的記憶體分配語句所在的檔名、行號為,我們注意到有一個比較陌生的資訊:{58}。這個整數值代表了什麼意思呢?

其實,它代表了第幾次記憶體分配操作。象這個例子,{58}代表了第58次記憶體分配操作發生了洩漏。你可能要說,我只new過一次,怎麼會是第58次?這很容易理解,其他的記憶體申請操作在C的初始化過程呼叫的唄。:)

有沒有可能,我們讓程式執行到第58次記憶體分配操作的時候,自動停下來,進入除錯狀態?所幸,crtdbg確實提供了這樣的函式:即 long _CrtSetBreakAlloc(long nAllocID)。我們加上它:

[html] view plain copy
  1. #define CRTDBG_MAP_ALLOC    
  2. #include <stdlib.h>    
  3. #include <crtdbg.h>    
  4.   
  5. int main()  
  6. {  
  7.     _CrtSetBreakAlloc(58);  
  8.     char* pChars = new char[10];  
  9.     _CrtDumpMemoryLeaks();  
  10.     return 0;  
  11. }  

你發現,程式執行到 char* pChars = new char[10];一句時,自動停下來進入除錯狀態。細細體會一下,你可以發現,這種方式你獲得的資訊遠比在程式退出時獲得檔名及行號有價值得多。因為報告洩漏檔名及行號,你獲得的只是靜態的資訊,然而_CrtSetBreakAlloc則是把整個現場恢復,你可以通過對函式呼叫棧分析(我發現很多人不習慣看函式呼叫棧,如果你屬於這種情況,我強烈推薦你去補上這一課,因為它太重要了)以及其他線上除錯技巧,來分析產生記憶體洩漏的原因。通常情況下,這種分析方法可以在5分鐘內找到肇事者。


PS:在VS2010下使用這兩種方法,巨集和標頭檔案不用包含也可以正確執行:
#define CRTDBG_MAP_ALLOC  
#include <stdlib.h>  
#include <crtdbg.h>   


 非MFC程式可以用以下方法檢測記憶體洩露:

1.程式開始包含如下定義:

#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif  // _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif  // _DEBUG

 

2.程式中新增下面的函式:

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);

 

Debug版本程式執行結束後如有記憶體洩漏,輸出視窗中會顯示類似資訊:
Detected memory leaks!
Dumping objects ->
g:\programs\test\test.cpp(16) : {51} client block at 0x00385C58, subtype 0, 4 bytes long.
 Data: <    > CD CD CD CD
Object dump complete.

 

 

MFC程式記憶體洩漏檢測方法:

 

1.在 CMyApp 中新增如下三個 CMemoryState 類的成員變數:

#ifdef _DEBUG
protected:
      CMemoryState m_msOld, m_msNew, m_msDiff;
#endif  // _DEBUG

 

2.在 CMyApp::InitInstance() 中新增如下程式碼:

#ifdef _DEBUG
      m_msOld.Checkpoint();
#endif  // _DEBUG

 

3.在 CMyApp::ExitInstance() 中新增如下程式碼:

#ifdef _DEBUG
      m_msNew.Checkpoint();
      if (m_msDiff.Difference(m_msOld, m_msNew))
      {
            afxDump<<"\nMemory Leaked :\n";
            m_msDiff.DumpStatistics();
            afxDump<<"Dump Complete !\n\n";
      }
#endif  // _DEBUG

 

Debug版本程式執行結束後如有記憶體洩漏,輸出視窗中會顯示類似資訊:

Memory Leaked :
0 bytes in 0 Free Blocks.
8 bytes in 1 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 8825 bytes.
Total allocations: 47506 bytes.
Dump Complete !

Detected memory leaks!
Dumping objects ->
g:\programs\chat\chatdlg.cpp(120) : {118} normal block at 0x00D98150, 8 bytes long.
 Data: <        > A8 7F D9 00 01 00 00 00
Object dump complete.

相關文章