Windows平臺下如何檢測C/C++記憶體洩露?
對於C/C++程式設計師來說,效率和優雅性大多數情況是對立的,我們經常會在這裡面抉擇,到底應該怎麼取捨。而說到效率,就不得不說讓這類程式設計師頭疼了N年的問題,記憶體洩露,至少從C/C++發明以來很多人都在頭疼。而Java/C#之類的語言並不存在這種問題,因為他們從堆中申請記憶體,根據引用計數等(據說有六種方法,大家可以搜一下Java垃圾回收器簡介)方法來管理記憶體,也就是傳說中的垃圾回收器(Garbage collector)。C/C++的程式設計師一向都是相信自己對記憶體的掌控能力,所以經常會出現各種記憶體洩露問題,有可能是因為自己疏忽大意,有可能是因為坑太深了,有可能是潛規則,總之,我們要做的是解決問題。
1.使用Window說自身的巨集_CrtDumpMemoryLeaks
例如我們有如下的程式碼:
#include <crtdbg.h> int _tmain(int argc, _TCHAR* argv[]) { int* pInt = new int[10]; _CrtDumpMemoryLeaks(); return 0; }
執行之後,在輸出視窗中會列印如下內容:
Detected memory leaks!
Dumping objects ->
{108} normal block at 0x003BA1C0, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
程式“[5364] AllTest.exe”已退出,返回值為 0 (0x0)。
他會告訴你檢測到記憶體洩露,洩露了多少位元組等內容。
遺憾的是看不到那裡記憶體洩露了,我們可以稍微改下這個程式碼,讓他支援在哪個檔案的哪個函式裡哪一行有記憶體洩露。修改內容如下:
//MemoryLeak_h // #ifdef _DEBUG #include <crtdbg.h> #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) #else #define DEBUG_NEW new #endif
#ifdef _DEBUG #include "MemoryLeak.h" #endif int _tmain(int argc, _TCHAR* argv[]) { int* pInt = DEBUG_NEW int[10]; _CrtDumpMemoryLeaks(); return 0; }
再次執行輸出視窗的內容如下:
Detected memory leaks!
Dumping objects ->
d:\project\alltest\alltest\alltest.cpp(12) : {108} normal block at 0x0060A1C0, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
程式“[6372] AllTest.exe”已退出,返回值為 0 (0x0)。
它標明瞭alltest.cpp第12行有記憶體洩露,洩露記憶體塊在0x0060A1C0位置,洩露大小為40位元組,這樣就能幫助我們快速的定位問題所在了。
2.Visual Leak Detector 庫(VLD)
專案主頁地址:http://vld.codeplex.com/,codeproject和sourceforge上都有其專案主頁,大家可以自行選擇下載。
其中包含Windows版本和原始碼版本,windows版本是標頭檔案和lib檔案,使用者可以自行呼叫,而原始碼版本可以自己編譯。
使用方法異常簡單,只需要包含vld.h標頭檔案即可
———- Block 58 at 0x004EBF58: 76 bytes ———-
Call Stack:
d:\project\alltest\alltest\alltest.cpp (9): AllTest.exe!wmain + 0x7 bytes
f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c (240): AllTest.exe!__tmainCRTStartup + 0x19 bytes
f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c (164): AllTest.exe!wmainCRTStartup
0x75C8339A (File and line number not available): kernel32.dll!BaseThreadInitThunk + 0x12 bytes
0x772C9EF2 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x63 bytes
0x772C9EC5 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x36 bytes
Data:
40 BA 4E 00 00 00 00 00 00 00 00 00 00 00 00 00 @.N….. ……..
28 00 00 00 01 00 00 00 3A 00 00 00 FD FD FD FD (……. :…….
CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD …….. ……..
CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD …….. ……..
CD CD CD CD CD CD CD CD FD FD FD FD …….. ……..
Visual Leak Detector detected 50 memory leaks (6904 bytes).
Largest number used: 11346 bytes.
Total allocations: 17030 bytes.
Visual Leak Detector is now exiting.
通過上面藍色加粗的字型我們就很容易得知具體的洩露位置了。而且VLD現在2.2.3的新版本有一個vld.ini檔案,用來配置各種開關和引數,具體的配置說明請檢視http://vld.codeplex.com/wikipage?title=Configuration%20Options&referringTitle=Documentation
注意:如果你發現你按照這種做法做了,但是卻出現0xc0150002的問題,請拷貝vld安裝目錄下的對應平臺的Microsoft.DTfW.DHL.manifest和dbghelp.dll到你的程式目錄下。(To ensure that vld.dll finds the required private assembly, you need to copy dbghelp.dll and Microsoft.DTfW.DHL.manifest to the same directory that vld.dll is in.)
最後不得不吐槽一下這種需要lib還需要dll的方法,甚至還需要對應mainfest檔案,太麻煩了,還不如直接給一個Lib完事了。