用VC++debugger檢測和隔離記憶體流失

來源:互聯網
上載者:User
 具有動態分配和釋放記憶體的能力是C/C++程式語言的重要特色之一。Visual C++ debugger 和 CRT庫提供了一系列有效檢測和評鑑記憶體流失的工具。
  
  設定記憶體流失檢測
  檢測記憶體流失的基本工具是調試器和CRT調試堆函數。為了使用調試堆函數,在你的程式中你必須含有下面的說明:
  #define _CRTDBG_MAP_ALLOC#include #include
  
  #include說明必須按順序說明。如果改變了順序,所用的函數可能不能正常工作。包含crtdbg.h的_malloc_dbg和 _free_dbg將 malloc和free函數映射到測試版中,它可以跟蹤記憶體的分配和釋放。這種映射僅僅在一個測試體系中發生(也就是說,僅僅當_DEBUG被定義的時候)。釋放的體系使用通常的malloc和 free功能。
  
  #define說明映射CRT堆函數的低級版本到相應的測試版本。這個說明是不需要的,但是沒有它,記憶體流失處含有的只是沒有多大用處的資訊。
  
  一旦你已經增加了剛才的說明,你能夠通過在你的程式中包含下面的說明來釋放記憶體資訊:
  _CrtDumpMemoryLeaks();
  
  當調試情況下運行程式時,在輸出視窗的Debug 標籤處_CrtDumpMemoryLeaks表現出記憶體流失的資訊。記憶體流失資訊類似下面這樣:
  Detected memory leaks!
  Dumping objects ->
  C:/PROGRAM FILES/VISUAL STUDIO/MyProjects/leaktest/leaktest.cpp(20) : {18} normal block at
  0x00780E80, 64 bytes long.
  Data: <         > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
  Object dump complete.
  
  如果你沒有用#define _CRTDBG_MAP_ALLOC說明,記憶體漏洞堆存處類似下面這樣:
  Detected memory leaks!
  Dumping objects ->
  {18} normal block at 0x00780E80, 64 bytes long.
  Data: <         > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
  Object dump complete.
  
  當_CRTDBG_MAP_ALLOC被定義時,_CrtDumpMemoryLeaks給了你更多的有用資訊。如果_CRTDBG_MAP_ALLOC沒有被定義,那麼將向你如下顯示:
  
  記憶體配置數值(花括弧內)
  模組的類型(normal、client或者CRT)
  以十六進位格式定位的記憶體
  以位元組計模組的大小
  第一個十六位元組的內容(也可以用十六進位)
  當定義了_CRTDBG_MAP_ALLOC的時候,顯示的內容也向你展現了出現泄漏記憶體所分配地方的檔案。在檔案名稱之後括弧內的數字(20,以此為例)是檔案內的行數值。如果你雙擊包含行數值和檔案名稱的輸出行,
  C:/PROGRAM FILES/VISUAL STUDIO/MyProjects/leaktest/leaktest.cpp(20) : {18} normal block at
  0x00780E80, 64 bytes long.
  
  指標將會跳到源檔案中記憶體被分配地方的行(在上面的情況下,leaktest.cpp的行號為20)。選擇輸出行並按F4將有同樣的效果。
  
  使用_CrtSetDbgFlag
  如果你的程式總是在同一各地方存在,那麼調用_CrtDumpMemoryLeaks時非常容易的。但是,如果你的程式需要在多個位置退出該怎麼辦?在每一個可能的出口處如果不調用_CrtDumpMemoryLeaks,你可在你的程式開始處包含下面的調用:
  _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  當程式退出時,這個說明自動地調用_CrtDumpMemoryLeaks。你必須設定兩個位域,_CRTDBG_ALLOC_MEM_DF和 _CRTDBG_LEAK_CHECK_DF。
  
  翻譯記憶體模組的類型
  記憶體流失資訊鑒別泄漏記憶體的每一個模組作為一個普通的模組、一個客戶模組或者一個CRT模組。實際上,普通的模組和客戶模組是你可能留心的唯一類型。
  一個普通模組(normal block)是由你的程式分配的普通記憶體。
  一個客戶模組(client block)是一種特殊的記憶體模組,它由於需要一個解構函式的對象而被Microsoft Foundation Classes (MFC)所使用。MFC new操作子建立一個普通模組或者一個客戶模組,來適合被建立的模組。
  一個CTR模組是由CRT庫提供自己使用而分配的記憶體模組。CRT庫對這些模組來管理自己的去分配,因此你不可能在記憶體流失報告中注意到這些,除非有些地方有嚴重的錯誤(例如,CRT庫崩潰)。
  在記憶體流失資訊中有兩種你從來沒有見過的模組類型:
  空閑模組(free block)是一種被釋放的記憶體模組
  Ignore block是你已經特殊標記過以至於在記憶體流失報告中不會出現的模組。
  
  設定CRT報告樣式
  像以前的一樣,按預設,_CrtDumpMemoryLeaks傾卸記憶體流失資訊到輸出視窗的Debug窗格。你可以運用_CrtSetReportMode重新設定它到堆存處,到另一個位置。如果你使用一個庫,它可能重新設定輸出到另一個位置。在這種情況下,你能夠利用下面的說明來設定輸出位置回到輸出視窗:
  _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );
  關於使用_CrtSetReportMode去發送輸出資訊到另一個位置,要看Visual C++檔案的_CrtSetReportMode節。
  
  在記憶體配置數目處設定一個斷點
  在記憶體流失報告中的檔案名稱和行號可告訴你泄漏的記憶體在那裡被分配,但是瞭解記憶體在那裡分配對於評鑑問題不總是充分的。在一個程式運行過程中,經常是一個分配將會被調用很多次,但是它可能在某次調用中泄漏記憶體。為了確定問題,你必須不但知道泄漏的記憶體在那裡分配,還要知道泄漏發生的條件。對你來說,使它成為可能的那條資訊是記憶體配置號。當那些被顯示的時候,檔案名稱和行號之後,這是在curly brace中出現的數值。例如,在下面的輸出中,"18"是記憶體配置號。它的意思是泄漏的記憶體是你程式中記憶體配置的第十八個模組。
  Detected memory leaks!
  Dumping objects ->
  C:/PROGRAM FILES/VISUAL STUDIO/MyProjects/leaktest/leaktest.cpp(20) : {18} normal block at
  0x00780E80, 64 bytes long.
  Data: <         > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
  Object dump complete.
  
  CRT庫計算在程式運行期間分配的所用記憶體模組,包括CRT自己分配的記憶體或者諸如MFC的其它模組。因此帶有分配號n的一個對象是在你的程式中分配的第n個對象,但不可能是由代碼分配的第n個對象。(在大部分情況下,它是不會的。)
  
  你可以利用分配號在記憶體配置的地方設定一個斷點。為了做這些,你可以距離你的程式開始很近處,設定一個位置斷點。當你的程式在那一點暫停時,你能夠從QuickWatch對話方塊或者Watch視窗設定這樣一個位置斷點。例如,在Watch視窗中,在Name欄鍵入下面的運算式:
  _crtBreakAlloc
  
  如果你正在用CRT庫的多線程的dynamic-link library (DLL)版本,你必須含有上下文操作符,像這裡說明的:
  {,,msvcrtd.dll}_crtBreakAlloc
  
  現在,按RETURN。調試器評估調用並且把結果放置在Value欄。如果你在記憶體配置過程中還沒有設定任何斷點,那麼這個值是-1。使用你想中斷處記憶體配置的分配數值來代替Value表中的值--例如,18 去中斷早期在輸出過程中展現的分配。
  
  當你在你感興趣的記憶體配置處設定斷點之後,你能夠繼續調試。在與從前相同的條件下,運行程式時一定要小心,因而分配的順序不會改變。當你的程式在一個特殊的記憶體配置點中斷的時候,你能夠查看Call Stack視窗和其他的測試資訊來確定在此條件下記憶體的分配。如果需要的話,你可以繼續從那一點執行程式,以至於瞭解對象到底發生了什麼事,同時還可能確定為了沒有正確地被去分配。(對對象設定一個資料斷點是很有協助的。)
  
  雖然在調試器中設定記憶體配置斷點通常更加容易,但是如果你喜歡的話,你可以在你的代碼中設定它們。為了在你的代碼中設定一個記憶體配置斷點,可以增加這樣一行(對於第十八個記憶體配置):
  _crtBreakAlloc = 18;
  
  最為一個選擇,你可以使用有相同效果的_CrtSetBreakAlloc函數。
  _CrtSetBreakAlloc(18);
  
  比較記憶體狀態
  定位記憶體流失的另一個方法就是在關鍵點對應用程式的記憶體狀態做快照。CRT庫提供了一個結構類型,_CrtMemState。你可以使用它來儲存記憶體狀態的一個快照。
  _CrtMemState s1, s2, s3;
  
  為了在特定點對記憶體狀態進行快照,可以傳遞一個_CrtMemState結構到he _CrtMemCheckpoint函數。此函數用當時記憶體狀態的一個快照來填充此結構:
  _CrtMemCheckpoint( &s1 );
  
  你可以通過傳遞此結構到_CrtMemDumpStatistics函數來傾卸_CrtMemState結構的任意點的內容:
  _CrtMemDumpStatistics( &s3 );( &s1 );
  
  此函數列印出類似於下面這樣的一堆記憶體配置資訊:
  0 bytes in 0 Free Blocks.
  0 bytes in 0 Normal Blocks.
  3071 bytes in 16 CRT Blocks.
  0 bytes in 0 Ignore Blocks.
  0 bytes in 0 Client Blocks.
  Largest number used: 3071 bytes.
  Total allocations: 3764 bytes.
  
  為了確定一個記憶體流失是否在一節代碼中出現,你可以在此節前和此節後對記憶體狀態作快照,然後用_CrtMemDifference比較兩種狀態:
  _CrtMemCheckpoint( &s1 );
  // memory allocations take place here
  _CrtMemCheckpoint( &s2 );
  
  if ( _CrtMemDifference( &s3, &s1, &s2) )
  _CrtMemDumpStatistics( &s3 );
  
  像名字暗示的一樣,_CrtMemDifference比較兩個記憶體狀態(最先的兩個參數)並且產生一個不同於這兩個狀態的結果(第三個參數)。在你的程式開始和結尾處的_CrtMemCheckpoint調用和使有_CrtMemDifference來比較結果為檢測記憶體流失提供了另一種方法。如果一個泄漏被檢測到,那麼可以使用_CrtMemCheckpoint調用來分割你的程式並且使用二元binary search technique來定位泄漏。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.