日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

内存泄漏定位

發(fā)布時(shí)間:2025/5/22 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 内存泄漏定位 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


今天調(diào)試程序,發(fā)現(xiàn)有內(nèi)存泄漏但是沒有提示具體是哪一行,搞得我很頭疼。

第一種:通過"OutPut窗口"定位引發(fā)內(nèi)存泄漏的代碼。

我們知道,MFC程序如果檢測到存在內(nèi)存泄漏,退出程序的時(shí)候會(huì)在調(diào)試窗口提醒內(nèi)存泄漏。例如:

class CMyApp : public CWinApp
{
public:
???BOOL InitApplication()
???{
???????int* leak = new int[10];
???????return TRUE;
???}
};

產(chǎn)生的內(nèi)存泄漏報(bào)告大體如下:

Detected memory leaks!
Dumping objects ->
c:\work\test.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,那么難道就沒有辦法?或者自己做?

呵呵,這不需要。其實(shí),MFC也沒有自己做。內(nèi)存泄漏檢測的工作是VC++的C運(yùn)行庫做的。也就是說,只要你是VC++程序員,都可以很方便地檢測內(nèi)存泄漏。我們還是給個(gè)樣例:

#include <crtdbg.h>

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

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

運(yùn)行(提醒:不要按Ctrl+F5,按F5),你將發(fā)現(xiàn),產(chǎn)生的內(nèi)存泄漏報(bào)告與MFC類似,但有細(xì)節(jié)不同,如下:

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.

為什么呢?看下面。

定位內(nèi)存泄漏由于哪一句話引起的

你已經(jīng)發(fā)現(xiàn)程序存在內(nèi)存泄漏。現(xiàn)在的問題是,我們要找泄漏的根源。

一般我們首先確定內(nèi)存泄漏是由于哪一句引起。在MFC中,這一點(diǎn)很容易。你雙擊內(nèi)存泄漏報(bào)告的文字,或者在Debug窗口中按F4,IDE就幫你定位到申請?jiān)搩?nèi)存塊的地方。對于上例,也就是這一句:

int* leak = new int[10];

這多多少少對你分析內(nèi)存泄漏有點(diǎn)幫助。特別地,如果這個(gè)new僅對應(yīng)一條delete(或者你把delete漏寫),這將很快可以確認(rèn)問題的癥結(jié)。

我們前面已經(jīng)看到,不使用MFC的時(shí)候,生成的內(nèi)存泄漏報(bào)告與MFC不同,而且你立刻發(fā)現(xiàn)按F4不靈。那么難道MFC做了什么手腳?

其實(shí)不是,我們來模擬下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];
}

再運(yùn)行這個(gè)樣例,你驚喜地發(fā)現(xiàn),現(xiàn)在內(nèi)存泄漏報(bào)告和MFC沒有任何分別了。

第二種方法:直接定位指定內(nèi)存塊錯(cuò)誤的代碼行(下面轉(zhuǎn))。

單確定了內(nèi)存泄漏發(fā)生在哪一行,有時(shí)候并不足夠。特別是同一個(gè)new對應(yīng)有多處釋放的情形。在實(shí)際的工程中,以下兩種情況很典型:

創(chuàng)建對象的地方是一個(gè)類工廠(ClassFactory)模式。很多甚至全部類實(shí)例由同一個(gè)new創(chuàng)建。對于此,定位到了new出對象的所在行基本沒有多大幫助。

COM對象。我們知道COM對象采用Reference Count維護(hù)生命周期。也就是說,對象new的地方只有一個(gè),但是Release的地方很多,你要一個(gè)個(gè)排除。

那么,有什么好辦法,可以迅速定位內(nèi)存泄漏?

答:有。

在內(nèi)存泄漏情況復(fù)雜的時(shí)候,你可以用以下方法定位內(nèi)存泄漏。這是我個(gè)人認(rèn)為通用的內(nèi)存泄漏追蹤方法中最有效的手段。

我們再回頭看看crtdbg生成的內(nèi)存泄漏報(bào)告:

Detected memory leaks!
Dumping objects ->
c:\work\test.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.

除了產(chǎn)生該內(nèi)存泄漏的內(nèi)存分配語句所在的文件名、行號為,我們注意到有一個(gè)比較陌生的信息:{52}。這個(gè)整數(shù)值代表了什么意思呢?

其實(shí),它代表了第幾次內(nèi)存分配操作。象這個(gè)例子,{52}代表了第52次內(nèi)存分配操作發(fā)生了泄漏。你可能要說,我只new過一次,怎么會(huì)是第52次?這很容易理解,其他的內(nèi)存申請操作在C的初始化過程調(diào)用的唄。:)

有沒有可能,我們讓程序運(yùn)行到第52次內(nèi)存分配操作的時(shí)候,自動(dòng)停下來,進(jìn)入調(diào)試狀態(tài)?所幸,crtdbg確實(shí)提供了這樣的函數(shù):即 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];
}

你發(fā)現(xiàn),程序運(yùn)行到 int* leak = new int[10]; 一句時(shí),自動(dòng)停下來進(jìn)入調(diào)試狀態(tài)。細(xì)細(xì)體會(huì)一下,你可以發(fā)現(xiàn),這種方式你獲得的信息遠(yuǎn)比在程序退出時(shí)獲得文件名及行號有價(jià)值得多。因?yàn)閳?bào)告泄漏文件名及行號,你獲得的只是靜態(tài)的信息,然而_CrtSetBreakAlloc則是把整個(gè)現(xiàn)場恢復(fù),你可以通過對函數(shù)調(diào)用棧分析(我發(fā)現(xiàn)很多人不習(xí)慣看函數(shù)調(diào)用棧,如果你屬于這種情況,我強(qiáng)烈推薦你去補(bǔ)上這一課,因?yàn)樗匾?#xff09;以及其他在線調(diào)試技巧,來分析產(chǎn)生內(nèi)存泄漏的原因。通常情況下,這種分析方法可以在5分鐘內(nèi)找到肇事者。

當(dāng)然,_CrtSetBreakAlloc要求你的程序執(zhí)行過程是可還原的(多次執(zhí)行過程的內(nèi)存分配順序不會(huì)發(fā)生變化)。這個(gè)假設(shè)在多數(shù)情況下成立。不過,在多線程的情況下,這一點(diǎn)有時(shí)難以保證。

個(gè)人心得:我在用這種方法時(shí)開始沒看懂,后來在MSDN中也找到了這方面相關(guān)的信息,后來才會(huì)用。我感覺在這方面網(wǎng)上介紹的不夠詳細(xì),下面我就相對詳細(xì)地解釋一下(為什么用“相對詳細(xì)”?本人比較懶)。首先說明一下,下面的函數(shù)不需要上面所添加的宏定義和"crtdbg.h"頭文件,也不需要EnableMemLeakCheck()函數(shù)。只需在main函數(shù)一開始運(yùn)行 _CrtSetBreakAlloc(long (4459))函數(shù)。其中4459是申請內(nèi)存的序號(上面有說明),然后F5運(yùn)行(不需要設(shè)斷點(diǎn)),然后會(huì)出現(xiàn)“Find Source”這個(gè)對話框,點(diǎn)擊“取消”。然后會(huì)出現(xiàn)“User breakpoint called from code at xxxx”的對話框,點(diǎn)擊“確定”,會(huì)看到一些匯編的代碼(不要怕,其實(shí)我也看不懂,雖然原來學(xué)過點(diǎn)匯編),調(diào)出堆棧窗口(call stack),在其中的“main() line xxx + xxx bytes”上雙擊(或它的上一行雙擊,我的上一行是一個(gè)自定義函數(shù),雙擊后直接定位到我new的地方,定位還是很準(zhǔn)的,開始我懷疑,但最后檢查果然是這地方?jīng)]釋放)會(huì)定位到錯(cuò)誤行。

第三種:用Ctrl+B來設(shè)定,不過現(xiàn)在好像忘了。效果根第二種方法基本一樣。

有人會(huì)問,既然第一種方法定位沒問題,為什么還要介紹第二種?其實(shí)在實(shí)際應(yīng)用中,某些內(nèi)存泄漏它沒有定位到哪一行的,只有內(nèi)存塊的序號(有可能我用的不太會(huì)用),這個(gè)時(shí)候就需要用第二種方法。


【轉(zhuǎn)自】http://www.uml.org.cn/c++/201110272.asp

總結(jié)

以上是生活随笔為你收集整理的内存泄漏定位的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。