1、在stdafx.h頭文件中添加兩行代碼//內存泄露檢測
#define _CRTDBG_MAP_ALLOC#include <stdlib.h>#include <crtdbg.h>
// 一般在入口函數一開始添加以下代碼 _CrtDumpMemoryLeaks(); _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
該語句在程序退出時自動調用 _CrtDumpMemoryLeaks。必須同時設置 _CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF 兩個位域,如上所示
介紹: 動態分配、回收內存是C/C++編程語言一個最強的特點,但是中國哲學家孫(Sun Tzu,我不知道是誰?那位知道?) 指出,最強的同時也是最弱的。這句話對C/C++應用來說非常正確,在內存處理出錯的地方通常就是BUGS產生的地方。一個最敏感和難檢測的BUG就是內存泄漏-沒有把前邊分配的內存成功釋放,一個小的內存泄漏可能不需要太注意,但是程序泄漏大塊內存,或者漸增式的泄漏內存可能引起的現象是:先是性能低下,再就是引起復雜的內存耗盡錯誤。最壞的是,一個內存泄漏程序可能用完了如此多的內存以至于引起其他的程序出錯,留給用戶的是不能知道錯誤到底來自哪里。另外,一個看上去無害的內存泄漏可能是另一個問題的先兆。幸運的是VC++DEBUGER和CRT庫提供了一組有效的檢測和定位內存泄漏的工具。本文描述如何使用這些工具有效和系統的排除內存泄漏。 啟動內存泄漏檢測: 主要的檢測工具是DEBUGER和CRT堆除錯函數。要使除錯函數生效,必須要在你的程序中包含以下幾個語句: #define _CRTDBG_MAP_ALLOC #include "stdlib.h" #include "crtdbg.h" 并且這些#include 語句必須按上邊給出的順序使用。如果你改變了順序,可能導致使用的函數工作不正常。包含crtdbg.h的作用是用malloc和free函數的debug版本(_malloc_dbg 和 _free_dbg)來替換他們,他們能跟蹤內存分配和回收。這個替換僅僅是在debug狀態下生效,Relese版本中還是使用普通的malloc和free函數?! ∩厦娴?define語句使用crt堆函數相應的debug版本來替換正常的堆函數。這個語句不是必需的,但是沒有他,你可能會失去一些有用的內存泄漏信息?! ∧阋坏┰谀愕某绦蛑性黾恿艘陨系恼Z句,你可以通過在程序中增加_CrtDumpMemoryLeaks();函數來輸出內存泄漏信息?! ‘斈阍赿ebuger下運行你的程序時,_CrtDumpMemoryLeaks 顯示內存泄漏信息在OutPut窗口的Debug標簽項里。內存泄漏信息舉例如下: Detected memory leaks! Dumping objects -> C:/PROGRAM FILES/VISUAL STUDIO/MyProjects/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. 如果你沒有使用 #define _CRTDBG_MAP_ALLOC語句的話,輸出信息將如下: 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 被定義后_CrtDumpMemoryLeaks給了你很多有用的信息。在沒有定義_CRTDBG_MAP_ALLOC 的情況下,顯示信息包含: 1.內存分配的編號(大括弧中的數字); 2.內存快的類型(普通型、客戶端型、CRT型); 3.16進制表示的內存位置; 4.內存快的大小; 5.前16bytes的內容?! ∪绻x了_CRTDBG_MAP_ALLOC ,輸出信息還包含當前泄漏內存是在那個文件中被分配的定位信息。文件名后圓括弧中的數字是行數。如果你雙擊這行信息, C:/PROGRAM FILES/VISUAL STUDIO/MyProjects/leaktest/leaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long. 光標就會跳轉到原文件中分配這個內存的行前。選擇Output中的題是行,按F4能達到同樣的效果?! ∈褂肬sing _CrtSetDbgFlag: 如果你的程序的退出點只有一個的話,調用_CrtDumpMemoryLeaks將是非常容易。但是,如果你的程序有多個退出點話會是什么樣一個情況?如果不想在每個退出點都調用_CrtDumpMemoryLeaks,你可以在程序的開始包含以下調用: _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
這個語句會在你的程序結束時自動調用_CrtDumpMemoryLeaks,但是你必須象前邊提到的那樣設置_CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF這兩個標志位?! 〗榻B一下內存塊的類型: 就象前面指出的,一個內存泄漏信息指出每個內存泄漏塊的類型為普通、客戶端或者CRT型。在實際程序中,普通型和客戶端型式最常見的類型。 普通型內存塊是你的程序平常分配的內存類型?! 】蛻舳诵蛢却鎵K是MFC程序給需要析構的對象分配的內存塊。MFC的new操作可以選擇普通型或客戶端型中合適的一種作為將要被創建的對象的內存塊類型?! RT內存塊是CRT庫為自己使用而分配的內存塊。CRT在處理自己的釋放內存操作時使用這些塊,所以在內存泄漏報告中這種類型并不常見,除非發生嚴重異常(例如:CRT庫出錯)?! ∵€有兩種類型你在內存泄漏信息中看不到: 自由塊,它是已經被釋放的內存塊; 忽略塊,它是已經被特殊標示的內存塊?! ≡O置CRT報告的格式: 在默認情況下,_CrtDumpMemoryLeaks輸出的內存泄漏信息就象前邊描述的那樣。你可以使用_CrtSetReportMode讓這些輸出信息輸出到其他地方。如果你使用一個庫,它可能要使輸出信息到其他的地方,在這種情況下,你可以使用_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );語句使輸出信息重新定位到Output窗口。 根據內存分配編號設置斷點: 內存泄漏報告中的文件名和行數告訴你內存泄漏的位置,但是知道內存泄漏位置不是總是能找到問題所在。在一個運行的程序中一個內存分配操作可能被調用多次,但是內存泄漏可能只發生在其中的某次操作中。為了確認問題所在,你除了知道泄漏的位置之外,你還必須要知道發生泄漏的條件。內存分配編號使得解決這個問題成為可能。這個數字就在文件名、行數之后的大括弧內。例如,在上面的輸出中“18”就是內存分配編號,它的意思是你程序中的內存泄漏發生在第18次分配操作中?! RT庫對正在運行程序中所有的內存塊分配進行計數,包括自身的內存分配,或者其他庫(象MFC)。一個對象的分配編號是n表示第n個對象被分配,但是它可能并不表示第N個對象通過代碼被分配(在大多數情況下它們并不相同)。 你可以根據內存分配編號在內存被分配的位置設置斷點。先在程序開始部分附近設置一個斷點,當你的程序在斷點處停止后,你可以通過QuickWatch對話框或者Watch窗口來設置內存分配斷點。在Watch窗口中的Name列中輸入_crtBreakAlloc,如果你使用的是多線程DLL版本的CRT庫的話你必須包含上下文轉換 {,,msvcrtd.dll}_crtBreakAlloc。完成后按回車,debugger處理這次調用,并且把返回值顯示在value列中。如果你沒有設置內存分配斷點的話返回值是-1。在value列中輸入你想設置的分配數,例如18?! ∧阍谧约焊信d趣的內存分配位置設置斷點后,你可以繼續debugging。細心的運行你的程序在相同的條件下,這樣才能保證內存分配的順序不致發生變化。當程序在特定的內存分配處停下來后, 你可以查看Call 窗口和其他的debugger信息來分析此次內存分配的條件。如果有必要你可以繼續運行程序,看一看這個對象有什么變化,或許可以得知為什么內存沒有被正確的釋放?! ”M管這個操作非常容易,但是如果你高興的話也可以在代碼中設置斷點。在代碼中增加一行代碼_crtBreakAlloc = 18;另外也可以通過_CrtSetBreakAlloc(18)來完成設置?! ”容^內存狀態 另一個定位內存泄漏的方法是在重要位置捕捉應用程序的“內存快照”。CRT庫提供了一個結構體類型 _CrtMemState,使用它你可以保存內存狀態的快照(當前狀態)?! CrtMemState s1, s2, s3; 為了得到一個快照,可以把一個_CrtMemState 結構體傳給_CrtMemCheckpoint 函數,這個函數可以把當前的內存狀態填充在結構體中: _CrtMemCheckpoint( &s1 ); 你可以通過把結構體_CrtMemState 傳給_CrtMemDumpStatistics函數來輸出結構體中的內容。 _CrtMemDumpStatistics( &s3 );( &s1 ); 它輸出的信息如下: 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函數來比較兩個狀態: _CrtMemCheckpoint( &s1 ); // memory allocations take place here _CrtMemCheckpoint( &s2 ); if ( _CrtMemDifference( &s3, &s1, &s2) ) _CrtMemDumpStatistics( &s3 ); 就像名字中暗示的那樣,_CrtMemDifference比較兩個內存狀態,并且產生一個結果(第一個參數)。把 _CrtMemCheckpoint 放在程序的開始和結尾,調用_CrtMemDifference 來比較結果,這也是一種檢測內存泄漏的方法。如果發現內存泄漏,你可以使用_CrtMemCheckpoint把程序分成兩半分別使用上述方法來檢測內存泄漏,這樣就是使用二分法來檢查內存泄漏。
可以封裝在一個類中,程序中直接調用: CDebug::Debug();===========================================================================// Debug.h: interface for the CDebug class.//---------------------------------------------------------------------------------// 內存泄露信息示例 : // {49} normal block at 0x00382F78, 40 bytes long.// Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD //---------------------------------------------------------------------------------// 顯示信息包含:// 1.內存分配的編號(大括弧中的數字);// 2.內存快的類型(普通型、客戶端型、CRT型);// <1>普通型內存塊是你的程序平常分配的內存類型。// <2>客戶端型內存塊是MFC程序給需要析構的對象分配的內存塊。// <3>CRT內存塊是CRT庫為自己使用而分配的內存塊。// <4>自由塊,它是已經被釋放的內存塊;// <5>忽略塊,它是已經被特殊標示的內存塊。// 3.16進制表示的內存位置;// 4.內存快的大?。?/ 5.前16bytes的內容。//////////////////////////////////////////////////////////////////////#if !defined(AFX_DEBUG_H__6B201A16_E36F_4830_A4F5_BD2207106871__INCLUDED_)#define AFX_DEBUG_H__6B201A16_E36F_4830_A4F5_BD2207106871__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000// 一般在入口函數cpp中添加以下定義和頭文件 CRT庫#define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> // 內存泄露信息中顯示文件名和代碼行號 如果出現重定義可以#ifdef _DEBUG#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)#endifclass CDebug {private:CDebug();virtual ~CDebug();public:static void Debug(){ // 一般在入口函數一開始添加以下代碼 _CrtDumpMemoryLeaks(); _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); };// 根據內存分配編號設置斷點:static void Debug(unsigned int num){//num就是剛剛檢測出來的內存泄露的地方大括號內的數字,跳轉到內存泄露的地方 _CrtSetBreakAlloc(num); }};#endif // !defined(AFX_DEBUG_H__6B201A16_E36F_4830_A4F5_BD2207106871__INCLUDED_)===========================================================================
新聞熱點
疑難解答