1. 程式人生 > >Vc 檢測記憶體洩漏

Vc 檢測記憶體洩漏

https://docs.microsoft.com/zh-cn/visualstudio/debugger/finding-memory-leaks-using-the-crt-library?view=vs-2017

 

啟用記憶體洩漏檢測

檢測記憶體洩漏是 C/c + + 偵錯程式和 C 執行時庫 (CRT) 的主要工具除錯堆函式。

若要啟用除錯堆的所有函式,在 c + + 程式中,按以下順序包含以下語句:

C++
#define _CRTDBG_MAP_ALLOC  
#include <stdlib.h> #include <crtdbg.h> 

#define 語句將 CRT 堆函式的基礎版本對映到對應的除錯版本。 如果您忽略#define語句,為記憶體洩漏轉儲不夠詳盡

包括crtdbg.h對映mallocfree到其除錯版本中,函式_malloc_dbg_free_dbg,它們將跟蹤記憶體分配和解除分配。 此對映只在包含 _DEBUG的除錯版本中發生。 釋出版本使用普通的 malloc 和 free 函式。

通過使用上面的語句啟用除錯堆函式後,將呼叫_CrtDumpMemoryLeaks之前應用程式退出時顯示的記憶體洩漏報告的應用程式退出點。

C++
_CrtDumpMemoryLeaks();  

如果您的應用程式有多個退出,無需手動設定_CrtDumpMemoryLeaks在每個退出點。 若要使自動呼叫_CrtDumpMemoryLeaks在每個退出點,將呼叫_CrtSetDbgFlag使用如下所示的位域對應用程式的開頭:

C++
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );  

預設情況下, _CrtDumpMemoryLeaks 將記憶體洩漏報告輸出到 “輸出”視窗的 “除錯”窗格中。 如果使用庫,該庫可能會將輸出重置到另一位置。

可以使用_CrtSetReportMode該報告重定向到其他位置,或返回到輸出視窗如下所示:

C++
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );  

解釋記憶體洩漏報告

如果應用沒有定義_CRTDBG_MAP_ALLOC, _CrtDumpMemoryLeaks顯示如下所示的記憶體洩漏報告:

cmd
Detected memory leaks!  
Dumping objects ->  
{18} normal block at 0x00780E80, 64 bytes long.  
 Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD Object dump complete. 

如果您的應用程式定義_CRTDBG_MAP_ALLOC,記憶體洩漏報告如下所示:

cmd
Detected memory leaks!  
Dumping objects ->  
c:\users\username\documents\projects\leaktest\leaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long. Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD Object dump complete. 

第二個報表顯示首次分配洩漏的記憶體的檔名和行號。

該值指示是否定義_CRTDBG_MAP_ALLOC,記憶體洩漏報告將顯示:

  • 記憶體分配編號,即18在示例
  • 塊型別,normal在示例中。
  • 十六進位制記憶體位置0x00780E80在示例中。
  • 塊的大小64 bytes在示例中。
  • 塊中前 16 個位元組的資料(十六進位制形式)。

記憶體塊型別正常客戶端,或CRT。 “普通塊”是由程式分配的普通記憶體。 “客戶端塊”是由 MFC 程式用於需要解構函式的物件的特殊型別記憶體塊。 MFC new 運算子根據正在建立的物件的需要建立普通塊或客戶端塊。

“CRT 塊”是由 CRT 庫為自己使用而分配的記憶體塊。 CRT 庫處理這些塊,解除分配,因此 CRT 塊不會顯示在記憶體洩漏報告中,除非使用 CRT 庫的嚴重問題。

記憶體洩漏報告中絕對不會出現另外兩個記憶體塊型別。 一個釋放的塊是已釋放,以便根據定義不會洩漏的記憶體。 忽略塊是已顯式標記要從記憶體洩漏報告中排除。

以前的技術確定記憶體洩漏的記憶體分配使用標準 CRTmalloc函式。 如果您的程式分配記憶體使用 c + +new運算子,但是,你可能只能看到檔名和行號位置operator new呼叫_malloc_dbg記憶體洩漏報告中。 若要建立更有用的記憶體洩漏報告,可以編寫如下所示來報告進行分配的行的巨集:

C++
#ifdef _DEBUG
    #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
    // Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the // allocations to be of _CLIENT_BLOCK type #else #define DBG_NEW new #endif 

現在可以替換new運算子使用DBG_NEW在程式碼中的巨集。 在除錯版本中,DBG_NEW使用的全域性過載operator new採用附加引數的塊型別、 檔案和行號。 過載new呼叫_malloc_dbg記錄的額外資訊。 記憶體洩漏報告顯示檔名和行號洩漏的物件的分配位置。 發行版本仍然使用預設值new。 下面是技術的示例:

C++
// debug_new.cpp
// compile by using: cl /EHsc /W4 /D_DEBUG /MDd debug_new.cpp
#define _CRTDBG_MAP_ALLOC
#include <cstdlib> #include <crtdbg.h> #ifdef _DEBUG #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) // Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the // allocations to be of _CLIENT_BLOCK type #else #define DBG_NEW new #endif struct Pod { int x; }; void main() { Pod* pPod = DBG_NEW Pod; pPod = DBG_NEW Pod; // Oops, leaked the original pPod! delete pPod; _CrtDumpMemoryLeaks(); } 

在 Visual Studio 中執行此程式碼時偵錯程式中呼叫_CrtDumpMemoryLeaks生成中的報表輸出看起來類似於的視窗:

Output
Detected memory leaks!
Dumping objects ->
c:\users\username\documents\projects\debug_new\debug_new.cpp(20) : {75}
 normal block at 0x0098B8C8, 4 bytes long.
 Data: <    > CD CD CD CD 
Object dump complete.

此輸出的第 20 行已洩漏的分配的報告debug_new.cpp

 Note

我們不建議建立一個名為的前處理器巨集new,或任何其他語言關鍵字。

記憶體分配編號上設定斷點

如果分配了洩漏記憶體塊,記憶體分配編號會通知你。 塊記憶體分配編號為 18,例如,是記憶體的應用程式執行期間分配的第 18 塊。 CRT 報告包含執行期間,其中包括 CRT 庫和 MFC 等其他庫由分配所有記憶體塊分配情況。 因此,記憶體分配塊編號 18 可能不是分配你的程式碼的第 18 記憶體塊。

可以使用分配編號在記憶體分配位置設定斷點。

若要設定使用監視視窗的記憶體分配斷點:

  1. 您的應用程式的起點附近設定斷點並開始除錯。

  2. 當應用程式會在斷點處暫停時,開啟Watch通過選擇視窗除錯 > Windows > 監視 1(或觀看 2,觀看 3,或觀看 4)。

  3. 在中Watch視窗中,鍵入_crtBreakAlloc中名稱列。

    如果您使用的多執行緒的 DLL 版本的 CRT 庫 (/MD 選項),新增上下文運算子: {,,ucrtbased.dll}_crtBreakAlloc

  4. 按 Enter。

    偵錯程式將計算呼叫,並將結果放入 “值”列。 此值將為為-1如果你尚未在記憶體分配上設定任何斷點。

  5. 在中值列中,值替換為要除錯程式執行中斷的記憶體分配的分配編號。

記憶體分配編號上設定斷點後,繼續除錯。 請確保在相同條件下執行,因此不會更改的記憶體分配編號。 當應用程式在指定的記憶體分配處中斷時,使用呼叫堆疊視窗和其他偵錯程式視窗來確定分配記憶體時的情況。 然後,可以繼續執行程式以觀察物件會發生什麼情況,並確定為什麼它不正確釋放。

在物件上設定資料斷點可能也有幫助。 有關詳細資訊,請參閱使用斷點

你也可以在程式碼中設定記憶體分配斷點。 您可以設定:

C++
_crtBreakAlloc = 18;  

或:

C++
_CrtSetBreakAlloc(18);  

比較記憶體狀態

定位記憶體洩漏的另一種技術涉及在關鍵點對應用程式的記憶體狀態拍快照。 若要在應用程式中給定點處的記憶體狀態的快照,建立_CrtMemState結構並將其傳遞給_CrtMemCheckpoint函式。

C++
_CrtMemState s1;  
_CrtMemCheckpoint( &s1 );  

_CrtMemCheckpoint函式填充在該結構的當前記憶體狀態的快照。

若要輸出的內容_CrtMemState結構,請將傳遞到結構_ CrtMemDumpStatistics函式:

C++
_CrtMemDumpStatistics( &s1 );  

_ CrtMemDumpStatistics 輸出記憶體狀態轉儲,如下所示:

cmd
0 bytes in 0 Free Blocks.  
0 bytes in 0 Normal Blocks. 3071 bytes in 16 CRT Blocks. 0 bytes in 0 Ignore Blocks. 0 bytes in 0 Client Blocks. Largest number used: 3071 bytes. Total allocations: 3764 bytes. 

若要確定在某個程式碼部分中是否發生了記憶體洩漏,可以對這部分之前和之後的記憶體狀態拍快照,然後使用 _ CrtMemDifference 比較兩個狀態:

C++
_CrtMemCheckpoint( &s1 );  
// memory allocations take place here  
_CrtMemCheckpoint( &s2 );  

if ( _CrtMemDifference( &s3, &s1, &s2) )  
   _CrtMemDumpStatistics( &s3 );  

_CrtMemDifference 比較記憶體狀態s1s2,並返回結果中的 (s3),它是之間的差異s1s2

查詢記憶體洩漏的一項技術開始上來_CrtMemCheckpoint的開頭和結尾的您的應用程式,然後使用在呼叫_CrtMemDifference可以比較的結果。 如果_CrtMemDifference顯示了記憶體洩漏,可以新增更多_CrtMemCheckpoint呼叫來劃分程式使用二進位制搜尋,直到你已隔離找到洩漏源。

誤報

_CrtDumpMemoryLeaks 如果庫將內部分配標記為普通塊而不是 CRT 塊或客戶端塊可能給出錯誤地指示記憶體洩漏。 在這種情況下, _CrtDumpMemoryLeaks 無法區分使用者分配和內部庫分配。 如果在 _CrtDumpMemoryLeaks呼叫點之後執行庫分配的全域性解構函式,則每個內部庫分配都會報告為記憶體洩漏。 版本早於可能會導致 Visual Studio.NET 的標準模板庫_CrtDumpMemoryLeaks以報告此類的假正值。

請參閱

CRT 除錯堆詳細資訊 
偵錯程式安全 
除錯本機程式碼

 

//======================================================================================================

其他

https://blogs.msdn.microsoft.com/vcblog/2015/10/21/memory-profiling-in-visual-c-2015/

https://blog.csdn.net/catshitone/article/details/79002823