使用_CRTDBG_LEAK_CHECK_DF檢查VC程序的內存泄漏(轉)

咱們知道,MFC程序若是檢測到存在內存泄漏,退出程序的時候會在調試窗口提醒內存泄漏。例如:html

class CMyApp : public CWinApp
{
public:
BOOL InitApplication()
{
int* leak = new int[10];
return TRUE;
}
};
產生的內存泄漏報告大致以下:程序員

Detected memory leaks!
Dumping objects ->
c:worktest.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
這挺好。問題是,若是咱們不喜歡MFC,那麼難道就沒有辦法?或者本身作?多線程

呵呵,這不須要。其實,MFC也沒有本身作。內存泄漏檢測的工做是VC++的C運行庫作的。也就是說,只要你是VC++程序員,均可以很方便地檢測內存泄漏。咱們仍是給個樣例:函數

#includepost

inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}線程

void main()
{
EnableMemLeakCheck();
int* leak = new int[10];
}
運行(提醒:不要按Ctrl+F5,按F5),你將發現,產生的內存泄漏報告與MFC相似,但有細節不一樣,以下:調試

Detected memory leaks!
Dumping objects ->
{52} normal block at 0x003C4410, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
爲何呢?看下面。orm

定位內存泄漏因爲哪一句話引發的
你已經發現程序存在內存泄漏。如今的問題是,咱們要找泄漏的根源。htm

通常咱們首先肯定內存泄漏是因爲哪一句引發。在MFC中,這一點很容易。你雙擊內存泄漏報告的文字,或者在Debug窗口中按F4,IDE就幫你定位到申請該內存塊的地方。對於上例,也就是這一句:對象

int* leak = new int[10];

這多多少少對你分析內存泄漏有點幫助。特別地,若是這個new僅對應一條delete(或者你把delete漏寫),這將很快能夠確認問題的癥結。

咱們前面已經看到,不使用MFC的時候,生成的內存泄漏報告與MFC不一樣,並且你馬上發現按F4不靈。那麼難道MFC作了什麼手腳?

其實不是,咱們來模擬下MFC作的事情。看下例:

inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}

#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

void main()
{
EnableMemLeakCheck();
int* leak = new int[10];
}

再運行這個樣例,你驚喜地發現,如今內存泄漏報告和MFC沒有任何分別了。

快速找到內存泄漏
單肯定了內存泄漏發生在哪一行,有時候並不足夠。特別是同一個new對應有多處釋放的情形。在實際的工程中,如下兩種狀況很典型:

建立對象的地方是一個類工廠(ClassFactory)模式。不少甚至所有類實例由同一個new建立。對於此,定位到了new出對象的所在行基本沒有多大幫助。

COM對象。咱們知道COM對象採用Reference Count維護生命週期。也就是說,對象new的地方只有一個,可是Release的地方不少,你要一個個排除。 
那麼,有什麼好辦法,能夠迅速定位內存泄漏?

答:有。

在內存泄漏狀況複雜的時候,你能夠用如下方法定位內存泄漏。這是我我的認爲通用的內存泄漏追蹤方法中最有效的手段。

咱們再回頭看看crtdbg生成的內存泄漏報告:

Detected memory leaks!
Dumping objects ->
c:worktest.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete. 
除了產生該內存泄漏的內存分配語句所在的文件名、行號爲,咱們注意到有一個比較陌生的信息:。這個整數值表明了什麼意思呢?

其實,它表明了第幾回內存分配操做。象這個例子,表明了第52次內存分配操做發生了泄漏。你可能要說,我只new過一次,怎麼會是第52次?這很容易理解,其餘的內存申請操做在C的初始化過程調用的唄。:)

有沒有可能,咱們讓程序運行到第52次內存分配操做的時候,自動停下來,進入調試狀態?所幸,crtdbg確實提供了這樣的函數:即 long _CrtSetBreakAlloc(long nAllocID)。咱們加上它:

inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}

#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

void main()
{
EnableMemLeakCheck();
_CrtSetBreakAlloc(52);
int* leak = new int[10];
}
你發現,程序運行到 int* leak = new int[10]; 一句時,自動停下來進入調試狀態。細細體會一下,你能夠發現,這種方式你得到的信息遠比在程序退出時得到文件名及行號有價值得多。由於報告泄漏文件名及行號,你得到的只是靜態的信息,然而_CrtSetBreakAlloc則是把整個現場恢復,你能夠經過對函數調用棧分析(我發現不少人不習慣看函數調用棧,若是你屬於這種狀況,我強烈推薦你去補上這一課,由於它過重要了)以及其餘在線調試技巧,來分析產生內存泄漏的緣由。一般狀況下,這種分析方法能夠在5分鐘內找到肇事者。

固然,_CrtSetBreakAlloc要求你的程序執行過程是可還原的(屢次執行過程的內存分配順序不會發生變化)。這個假設在多數狀況下成立。不過,在多線程的狀況下,這一點有時難以保證。

 

原文轉載自:http://www.cnblogs.com/qq78292959/archive/2011/05/28/2076605.html

相關文章
相關標籤/搜索