1. 程式人生 > >Windows平臺下如何檢測C/C++記憶體洩露?

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完事了。