有些內存泄露是不會dump出來詳細信息的,只會給出內存塊號,這種情況一下一般可以用一下方法調試出來。
這兩天調一個程序,發現每次退出都有內存泄漏,在此總結一個調試內存泄漏的好方法。
對於比較明顯的內存泄漏,vc2008是比較容易定位的,在Debug模式F5運行,退出時會列出引起內存泄漏的問題代碼所在文件及行號;但如果是比較隱蔽的內存泄漏,Output輸出窗口只會輸出部分信息,形式如下:
Detected memory leaks!
Dumping objects ->
{858} normal block at 0x003ED290, 12 bytes long.
Data: < \H > A0 5C 48 00 00 00 00 00 00 00 00 00
Object dump complete.
我們來看一下輸出信息的格式。
{xxx} — 表示程序運行后第xxx次內存分配發生了泄漏,即內存分配編號;
xxx block — 表示內存塊類型,包括三種:普通(normal)、客戶端(client)和運行時(CRT);
at 0xFFFFFFFF — 表示發生泄漏的內存地址,用十六進制表示;
xx bytes long — 表示發生泄漏的內存大小;
Data:xxx — 表示內存數據信息,一般輸出前16字節的內容。
內存泄漏的地址、大小和數據信息很難對我們的定位起到有效地幫助,但是如果我們能夠讓程序停在分配有泄漏的內存的地方,然后根據調用堆棧(Call Stack)里的信息,就可以很方便的定位是哪個語句出現問題。幸運的是,VC的C運行庫(C run-time library)為我們提供了一個這樣的函數:CrtSetBreakAlloc,聲明如下:
long _CrtSetBreakAlloc( long lBreakAlloc );
其中參數lBreakAlloc即為內存分配號(花括號中的數字)。我們只要在程序初始化函數中添加語句:_CrtSetBreakAlloc(858)就可以使程序在Debug F5運行時自動斷點到我們想要的位置。
使用這個函數進行內存泄漏定位有個條件,即發生內存泄漏的分配號是固定的,每次啟動程序不會隨機變化。
但是如果我們的程序比較大,內存泄漏也不止一個,用上面的方法調試可能會很麻煩,需要經常更新代碼並重新編譯程序。還有更好的方法。
首先確定自己的程序使用的是哪個C運行庫,方法是程序Propertise -> C/C++ -> Code Generation -> Runtime Library選項,若為Multi-threaded Debug(/MTd),則為靜態鏈接,若為Multi-threaded Debug DLL(/MDd),則為動態鏈接。
然后按F11啟動程序,程序會停在入口點。此時調出Watch窗口,
靜態鏈接 在name項中輸入_crtBreakAlloc,在value項中輸入你要定位的內存分配編號;
動態鏈接 在name項中輸入{,,msvcr90d.dll}_crtBreakAlloc,在value項中輸入內存分配號。注意msvcr90d.dll是vc2008環境使用的運行庫dll,若你用的是其他版本vc,請換成對應版本的dll。
現在按F5運行,程序就會自動定位到你要調試的內存分配語句,這時候看call stack的信息會很有幫助。
這個辦法使用起來非常靈活,只要你確定內存分配號是不變的,就可以方便地為程序設置斷點了,省去了更新代碼和編譯的麻煩。這個方法幾乎是當前進行內存泄漏調試的最有效方法,如果你發現內存泄漏的分配號是變化的,可以想辦法去掉一些沒有問題的內存申請(多半是界面和多線程涉及到的申請),盡量使有問題的分配固定,就可以用這個方法排錯了。