日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

VC++ 6.0 中如何使用 CRT 调试功能来检测内存泄漏[转]

發(fā)布時(shí)間:2025/3/14 c/c++ 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 VC++ 6.0 中如何使用 CRT 调试功能来检测内存泄漏[转] 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

/C++ 編程語言的最強(qiáng)大功能之一便是其動(dòng)態(tài)分配和釋放內(nèi)存,但是中國有句古話:“最大的長(zhǎng)處也可能成為最大的弱點(diǎn)”,那么 C/C++ 應(yīng)用程序正好印證了這句話。在 C/C++ 應(yīng)用程序開發(fā)過程中,動(dòng)態(tài)分配的內(nèi)存處理不當(dāng)是最常見的問題。其中,最難捉摸也最難檢測(cè)的錯(cuò)誤之一就是內(nèi)存泄漏,即未能正確釋放以前分配的內(nèi)存的錯(cuò)誤。偶爾發(fā)生的少量?jī)?nèi)存泄漏可能不會(huì)引起我們的注意,但泄漏大量?jī)?nèi)存的程序或泄漏日益增多的程序可能會(huì)表現(xiàn)出各種 各樣的征兆:從性能不良(并且逐漸降低)到內(nèi)存完全耗盡。更糟的是,泄漏的程序可能會(huì)用掉太多內(nèi)存,導(dǎo)致另外一個(gè)程序垮掉,而使用戶無從查找問題的真正根源。此外,即使無害的內(nèi)存泄漏也可能殃及池魚。
  幸運(yùn)的是,Visual Studio 調(diào)試器和 C 運(yùn)行時(shí) (CRT) 庫為我們提供了檢測(cè)和識(shí)別內(nèi)存泄漏的有效方法。下面請(qǐng)和我一起分享收獲——如何使用 CRT 調(diào)試功能來檢測(cè)內(nèi)存泄漏?

  • 如何啟用內(nèi)存泄漏檢測(cè)機(jī)制?
    • 使用 _CrtSetDbgFlag
    • 設(shè)置 CRT 報(bào)告模式
  • 解釋內(nèi)存塊類型
  • 如何在內(nèi)存分配序號(hào)處設(shè)置斷點(diǎn)?
  • 如何比較內(nèi)存狀態(tài)?
  • 結(jié)論
  • 如何啟用內(nèi)存泄漏檢測(cè)機(jī)制?

      VC++ IDE 的默認(rèn)狀態(tài)是沒有啟用內(nèi)存泄漏檢測(cè)機(jī)制的,也就是說即使某段代碼有內(nèi)存泄漏,調(diào)試會(huì)話的 Output 窗口的 Debug 頁不會(huì)輸出有關(guān)內(nèi)存泄漏信息。你必須設(shè)定兩個(gè)最基本的機(jī)關(guān)來啟用內(nèi)存泄漏檢測(cè)機(jī)制。

    一是使用調(diào)試堆函數(shù):

    #define _CRTDBG_MAP_ALLOC #include<stdlib.h> #include<crtdbg.h>

    注意:#include 語句的順序。如果更改此順序,所使用的函數(shù)可能無法正確工作。

      通過包含 crtdbg.h 頭文件,可以將 malloc 和 free 函數(shù)映射到其“調(diào)試”版本 _malloc_dbg 和 _free_dbg,這些函數(shù)會(huì)跟蹤內(nèi)存分配和釋放。此映射只在調(diào)試(Debug)版本(也就是要定義 _DEBUG)中有效。發(fā)行版本(Release)使用普通的 malloc 和 free 函數(shù)。
      #define 語句將 CRT 堆函數(shù)的基礎(chǔ)版本映射到對(duì)應(yīng)的“調(diào)試”版本。該語句不是必須的,但如果沒有該語句,那么有關(guān)內(nèi)存泄漏的信息會(huì)不全。

    二是在需要檢測(cè)內(nèi)存泄漏的地方添加下面這條語句來輸出內(nèi)存泄漏信息:

    _CrtDumpMemoryLeaks();

      當(dāng)在調(diào)試器下運(yùn)行程序時(shí),_CrtDumpMemoryLeaks 將在 Output 窗口的 Debug 頁中顯示內(nèi)存泄漏信息。比如:

    Detected memory leaks! Dumping objects -> C:\Temp\memleak\memleak.cpp(15) : {45} normal block at 0x00441BA0, 2 bytes long. Data: <AB> 41 42 c:\program files\microsoft visual studio\vc98\include\crtdbg.h(552) : {44} normal block at 0x00441BD0, 33 bytes long. Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD c:\program files\microsoft visual studio\vc98\include\crtdbg.h(552) : {43} normal block at 0x00441C20, 40 bytes long. Data: < C > 08 02 43 00 16 00 00 00 00 00 00 00 00 00 00 00 Object dump complete.

    如果不使用 #define _CRTDBG_MAP_ALLOC 語句,內(nèi)存泄漏的輸出是這樣的:

    Detected memory leaks! Dumping objects -> {45} normal block at 0x00441BA0, 2 bytes long. Data: <AB> 41 42 {44} normal block at 0x00441BD0, 33 bytes long. Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD {43} normal block at 0x00441C20, 40 bytes long. Data: < C > C0 01 43 00 16 00 00 00 00 00 00 00 00 00 00 00 Object dump complete.

      根據(jù)這段輸出信息,你無法知道在哪個(gè)源程序文件里發(fā)生了內(nèi)存泄漏。下面我們來研究一下輸出信息的格式。第一行和第二行沒有什么可說的,從第三行開始:

    ?

    xx}:花括弧內(nèi)的數(shù)字是內(nèi)存分配序號(hào),本文例子中是 {45},{44},{43}; block:內(nèi)存塊的類型,常用的有三種:normal(普通)、client(客戶端)或 CRT(運(yùn)行時(shí));本文例子中是:normal block; 用十六進(jìn)制格式表示的內(nèi)存位置,如:at 0x00441BA0 等; 以字節(jié)為單位表示的內(nèi)存塊的大小,如:32 bytes long; 前 16 字節(jié)的內(nèi)容(也是用十六進(jìn)制格式表示),如:Data: <AB> 41 42 等;

      仔細(xì)觀察不難發(fā)現(xiàn),如果定義了 _CRTDBG_MAP_ALLOC ,那么在內(nèi)存分配序號(hào)前面還會(huì)顯示在其中分配泄漏內(nèi)存的文件名,以及文件名后括號(hào)中的數(shù)字表示發(fā)生泄漏的代碼行號(hào),比如:

    C:\Temp\memleak\memleak.cpp(15)

      雙擊 Output 窗口中此文件名所在的輸出行,便可跳到源程序文件分配該內(nèi)存的代碼行(也可以選中該行,然后按 F4,效果一樣) ,這樣一來我們就很容易定位內(nèi)存泄漏是在哪里發(fā)生的了,因此,_CRTDBG_MAP_ALLOC 的作用顯而易見。

    使用 _CrtSetDbgFlag

      如果程序只有一個(gè)出口,那么調(diào)用 _CrtDumpMemoryLeaks 的位置是很容易選擇的。但是,如果程序可能會(huì)在多個(gè)地方退出該怎么辦呢?在每一個(gè)可能的出口處調(diào)用 _CrtDumpMemoryLeaks 肯定是不可取的,那么這時(shí)可以在程序開始處包含下面的調(diào)用:

    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

      這條語句無論程序在什么地方退出都會(huì)自動(dòng)調(diào)用 _CrtDumpMemoryLeaks。注意:這里必須同時(shí)設(shè)置兩個(gè)位域標(biāo)志:_CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF。

    設(shè)置 CRT 報(bào)告模式

      默認(rèn)情況下,_CrtDumpMemoryLeaks 將內(nèi)存泄漏信息 dump 到 Output 窗口的 Debug 頁, 如果你想將這個(gè)輸出定向到別的地方,可以使用 _CrtSetReportMode 進(jìn)行重置。如果你使用某個(gè)庫,它可能將輸出定向到另一位置。此時(shí),只要使用以下語句將輸出位置設(shè)回 Output 窗口即可:

    _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );

    有關(guān)使用 _CrtSetReportMode 的詳細(xì)信息,請(qǐng)參考 MSDN 庫關(guān)于 _CrtSetReportMode 的描述。

    解釋內(nèi)存塊類型

      前面已經(jīng)說過,內(nèi)存泄漏報(bào)告中把每一塊泄漏的內(nèi)存分為 normal(普通塊)、client(客戶端塊)和 CRT 塊。事實(shí)上,需要留心和注意的也就是 normal 和 client,即普通塊和客戶端塊。

    • normal block(普通塊):這是由你的程序分配的內(nèi)存。
    • client block(客戶塊):這是一種特殊類型的內(nèi)存塊,專門用于 MFC 程序中需要析構(gòu)函數(shù)的對(duì)象。MFC new 操作符視具體情況既可以為所創(chuàng)建的對(duì)象建立普通塊,也可以為之建立客戶塊。
    • CRT block(CRT 塊):是由 C RunTime Library 供自己使用而分配的內(nèi)存塊。由 CRT 庫自己來管理這些內(nèi)存的分配與釋放,我們一般不會(huì)在內(nèi)存泄漏報(bào)告中發(fā)現(xiàn) CRT 內(nèi)存泄漏,除非程序發(fā)生了嚴(yán)重的錯(cuò)誤(例如 CRT 庫崩潰)。

    除了上述的類型外,還有下面這兩種類型的內(nèi)存塊,它們不會(huì)出現(xiàn)在內(nèi)存泄漏報(bào)告中:

    • free block(空閑塊):已經(jīng)被釋放(free)的內(nèi)存塊。
    • Ignore block(忽略塊):這是程序員顯式聲明過不要在內(nèi)存泄漏報(bào)告中出現(xiàn)的內(nèi)存塊。

    如何在內(nèi)存分配序號(hào)處設(shè)置斷點(diǎn)?

      在內(nèi)存泄漏報(bào)告中,的文件名和行號(hào)可告訴分配泄漏的內(nèi)存的代碼位置,但僅僅依賴這些信息來了解完整的泄漏原因是不夠的。因?yàn)橐粋€(gè)程序在運(yùn)行時(shí),一段分配內(nèi)存的代碼可能會(huì)被調(diào)用很多次,只要有一次調(diào)用后沒有釋放內(nèi)存就會(huì)導(dǎo)致內(nèi)存泄漏。為了確定是哪些內(nèi)存沒有被釋放,不僅要知道泄漏的內(nèi)存是在哪里分配的,還要知道泄漏產(chǎn)生的條件。這時(shí)內(nèi)存分配序號(hào)就顯得特別有用——這個(gè)序號(hào)就是文件名和行號(hào)之后的花括弧里的那個(gè)數(shù)字。
      例如,在本文例子代碼的輸出信息中,“45”是內(nèi)存分配序號(hào),意思是泄漏的內(nèi)存是你程序中分配的第四十五個(gè)內(nèi)存塊:

    Detected memory leaks! Dumping objects -> C:\Temp\memleak\memleak.cpp(15) : {45} normal block at 0x00441BA0, 2 bytes long. Data: <AB> 41 42 ...... Object dump complete.

      CRT 庫對(duì)程序運(yùn)行期間分配的所有內(nèi)存塊進(jìn)行計(jì)數(shù),包括由 CRT 庫自己分配的內(nèi)存和其它庫(如 MFC)分配的內(nèi)存。因此,分配序號(hào)為 N 的對(duì)象即為程序中分配的第 N 個(gè)對(duì)象,但不一定是代碼分配的第 N 個(gè)對(duì)象。(大多數(shù)情況下并非如此。)
      這樣的話,你便可以利用分配序號(hào)在分配內(nèi)存的位置設(shè)置一個(gè)斷點(diǎn)。方法是在程序起始附近設(shè)置一個(gè)位置斷點(diǎn)。當(dāng)程序在該點(diǎn)中斷時(shí),可以從 QuickWatch(快速監(jiān)視)對(duì)話框或 Watch(監(jiān)視)窗口設(shè)置一個(gè)內(nèi)存分配斷點(diǎn):

      例如,在 Watch 窗口中,在 Name 欄鍵入下面的表達(dá)式:

    _crtBreakAlloc

    如果要使用 CRT 庫的多線程 DLL 版本(/MD 選項(xiàng)),那么必須包含上下文操作符,像這樣:

    {,,msvcrtd.dll}_crtBreakAlloc

      現(xiàn)在按下回車鍵,調(diào)試器將計(jì)算該值并把結(jié)果放入 Value 欄。如果沒有在內(nèi)存分配點(diǎn)設(shè)置任何斷點(diǎn),該值將為 –1。
      用你想要在其位置中斷的內(nèi)存分配的分配序號(hào)替換 Value 欄中的值。例如輸入 45。這樣就會(huì)在分配序號(hào)為 45 的地方中斷。
      在所感興趣的內(nèi)存分配處設(shè)置斷點(diǎn)后,可以繼續(xù)調(diào)試。這時(shí),運(yùn)行程序時(shí)一定要小心,要保證內(nèi)存塊分配的順序不會(huì)改變。當(dāng)程序在指定的內(nèi)存分配處中斷時(shí),可以查看 Call Stack(調(diào)用堆棧)窗口和其它調(diào)試器信息以確定分配內(nèi)存時(shí)的情況。如果必要,可以從該點(diǎn)繼續(xù)執(zhí)行程序,以查看對(duì)象發(fā)生了什么情況,或許可以確定未正確釋放對(duì)象的原因。
      盡管通常在調(diào)試器中設(shè)置內(nèi)存分配斷點(diǎn)更方便,但如果愿意,也可在代碼中設(shè)置這些斷點(diǎn)。為了在代碼中設(shè)置一個(gè)內(nèi)存分配斷點(diǎn),可以增加這樣一行(對(duì)于第四十五個(gè)內(nèi)存分配):

    _crtBreakAlloc = 45;

    你還可以使用有相同效果的 _CrtSetBreakAlloc 函數(shù):

    _CrtSetBreakAlloc(45);

    如何比較內(nèi)存狀態(tài)?

      定位內(nèi)存泄漏的另一個(gè)方法就是在關(guān)鍵點(diǎn)獲取應(yīng)用程序內(nèi)存狀態(tài)的快照。CRT 庫提供了一個(gè)結(jié)構(gòu)類型 _CrtMemState。你可以用它來存儲(chǔ)內(nèi)存狀態(tài)的快照:

    _CrtMemState s1, s2, s3;

      若要獲取給定點(diǎn)的內(nèi)存狀態(tài)快照,可以向 _CrtMemCheckpoint 函數(shù)傳遞一個(gè) _CrtMemState 結(jié)構(gòu)。該函數(shù)用當(dāng)前內(nèi)存狀態(tài)的快照填充此結(jié)構(gòu):

    _CrtMemCheckpoint( &s1 );

      通過向 _CrtMemDumpStatistics 函數(shù)傳遞 _CrtMemState 結(jié)構(gòu),可以在任意地方 dump 該結(jié)構(gòu)的內(nèi)容:

    _CrtMemDumpStatistics( &s1 );

    該函數(shù)輸出如下格式的 dump 內(nèi)存分配信息:

    0 bytes in 0 Free Blocks. 75 bytes in 3 Normal Blocks. 5037 bytes in 41 CRT Blocks. 0 bytes in 0 Ignore Blocks. 0 bytes in 0 Client Blocks. Largest number used: 5308 bytes. Total allocations: 7559 bytes.

      若要確定某段代碼中是否發(fā)生了內(nèi)存泄漏,可以通過獲取該段代碼之前和之后的內(nèi)存狀態(tài)快照,然后使用 _CrtMemDifference 比較這兩個(gè)狀態(tài):

    _CrtMemCheckpoint( &s1 );// 獲取第一個(gè)內(nèi)存狀態(tài)快照// 在這里進(jìn)行內(nèi)存分配_CrtMemCheckpoint( &s2 );// 獲取第二個(gè)內(nèi)存狀態(tài)快照// 比較兩個(gè)內(nèi)存快照的差異 if ( _CrtMemDifference( &s3, &s1, &s2) )_CrtMemDumpStatistics( &s3 );// dump 差異結(jié)果

      顧名思義,_CrtMemDifference 比較兩個(gè)內(nèi)存狀態(tài)(前兩個(gè)參數(shù)),生成這兩個(gè)狀態(tài)之間差異的結(jié)果(第三個(gè)參數(shù))。在程序的開始和結(jié)尾放置 _CrtMemCheckpoint 調(diào)用,并使用 _CrtMemDifference 比較結(jié)果,是檢查內(nèi)存泄漏的另一種方法。如果檢測(cè)到泄漏,則可以使用 _CrtMemCheckpoint 調(diào)用通過二進(jìn)制搜索技術(shù)來分割程序和定位泄漏。

    結(jié)論

      盡管 VC ++ 具有一套專門調(diào)試 MFC 應(yīng)用程序的機(jī)制,但本文上述討論的內(nèi)存分配很簡(jiǎn)單,沒有涉及到 MFC 對(duì)象,所以這些內(nèi)容同樣也適用于 MFC 程序。在 MSDN 庫中可以找到很多有關(guān) VC++ 調(diào)試方面的資料,如果你能善用 MSDN 庫,相信用不了多少時(shí)間你就有可能成為調(diào)試高手。

    本人水平不高,謬誤在所難免,請(qǐng)大家拍磚,不要客氣。順祝大家圣誕快樂!

    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/xuyu/archive/2009/11/30/1613841.html

    總結(jié)

    以上是生活随笔為你收集整理的VC++ 6.0 中如何使用 CRT 调试功能来检测内存泄漏[转]的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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