本文章是由機器翻譯。

深究 CLR

.NET 記憶體回收堆積程式碼剖析

S。Ramaswamy (& S)V。Morrison

MSDN Magazine 6 月 2009年文件 」的.NET 應用程式稽核記憶體使用量」,我們會討論使用工具,例如工作管理員]、 [PerfMon] 和 [VADump 監視記憶體耗用量。這些工具協助您監視應用程式的整體的記憶體消耗量。通常,如果.NET 應用程式正在使用大量的記憶體,則是因為應用程式載入大量的 DLL 或應用程式在.NET 記憶體回收 (GC) 堆積上建立大量的存留期長的物件。如果應用程式載入的 DLL,唯一 recourse (以外若要避免不必要的相依性) 就是執行較少的程式碼。但是,如果應用程式有較大的 GC 堆積耗用量,一個可以識別 GC 堆積 (Heap) 已造成大幅耗用記憶體在 6 月發行項中所述。此的文件中我們可以完成 GC 堆積相關的記憶體問題的圖片上使用.NET GC 堆積記憶體調查 CLR 分析工具提供逐步指示。

重點:稽核所有的應用程式

.NET 執行階段擷取某些特定作業 (例如,配置記憶體或使用大型的類別程式庫),它不一定容易認為這些作業對應用程式效能的影響。是特別如此的記憶體。takeaway 最後一個文件中的重點是每個應用程式需要定期記憶體稽核。這份文件會穿過這逐步使用真實程式為我們的範例 (相對於示範)。這是一個簡單的程式,然後我們可以有選擇不是要稽核應用程式 ’s 記憶體使用量。但是,完成記憶體稽核我們,執行稽核的小努力支付關閉 handsomely,我們找到大量的記憶體無效率。

我們在這份文件中使用的程式稱為 XMLView 即是高度可調適檢視的 XML 檔的簡單器。XMLView 可以輕鬆地處理 gigabytes 大小中的 XML 檔案 (就試試看在 IE 中!) 由於這個應用程式設計為從開始有效的記憶體。儘管小心設計某些的情況下工具類似 [工作管理員會顯示應用程式使用比我們會預期的明顯更多記憶體。這份文件引導您發現效能問題的調查。

大圖片記憶體使用量

記憶體稽核第一個步驟是發射應用程式,看看 OS 工作管理員來檢查應用程式 ’s 整體記憶體使用量。如我們先前所述,公制最重要的是"記憶體-私人工作集]資料行。這是指不能在電腦上執行其他處理程序與共用之應用程式記憶體 (請參閱先前的文件以取得詳細資料)。

工作管理員] 開啟後您可以監視即時的應用程式的私用記憶體使用方式、 透過不同的使用者案例執行,並觀察記憶體耗用量對任何影響。有兩個異常狀況保證您的注意。是記憶體耗用量,但找到工作正在執行的案例。就例如開啟功能表項目,或執行簡單的作業應該不會造成大型的尖峰記憶體耗用量。記憶體遺漏 (Memory Leak) 是異常的必須注意的第二個型別。某些作業,例如開啟新的檔案在預期的情況為新的資料結構將會建立每次執行作業。但是,許多作業,就例如是搜尋),不會以邏輯方式配置新長時間執行的記憶體。不配置的記憶體 (如延遲的資料結構會填入) 執行第一次這些沒有狀態 (Stateless) 作業的不尋常。但是,如果記憶體使用量持續增加的案例的後續反覆項目,記憶體遺漏,而且需要進行調查。這項技術的使用 [工作管理員是相當粗略的設計 (只大量記憶體的問題可能會明顯),但對於許多應用程式而言大的價值有關。

對於 XMLView,案例是啟動一個大的 XML 檔案檢視器,以及執行例如開啟樹狀節點,或搜尋各種瀏覽作業。這項實驗中一開始開啟 (記憶體使用量穩定後一段中 ; 沒有遺漏請參閱的圖 1)。記憶體使用量而大幅 spiked (8 MB),[搜尋] 功能時使用 16812KB 私人工作集 (請參閱的圖 2) 來 25648KB。這表示我們要尋找的異常狀況的其中一個:資源消耗不適合我們的心理模型的程式 ’s 記憶體的行為,因為搜尋有沒有輔助的資料結構,因此應該不會耗用額外的記憶體。若要解決的神秘我們。


圖 1 XMLView 記憶體使用量工作管理員] 中搜尋作業之前


圖 2 XMLView 記憶體使用量工作管理員] 中搜尋作業之後

CLR 程式碼剖析工具

許多協力廠商的工具可供進行分析的.NET GC 堆積 ;但是,我們將著重 CLR 分析工具優點是能夠使用可用的網頁下載,從 Microsoft。"CLR 分析工具 」 網站搜尋會找出下載網站,並安裝包含直接解壓縮的檔案 (即沒有沒有正式的安裝步驟)。包含封裝的一部份,檔 CLR Profiler.doc 會提供文件。此的文件中我們將重點放在經常使用的組件。

CLR 程式碼剖析工具如何運作?

為了要正確地使用程式碼剖析工具,您必須瞭解如何收集這些資料和工具的限制。CLR 分析工具使用一個特殊的介面,讓它以便回呼 (msdn.microsoft.com/library/ms404511.aspx) 在執行階段內的特定事件發生時執行階段。目前,正在分析程序啟動特定環境變數設定,通知有關程式碼剖析工具連絡人機制執行階段時,才可以使用這個介面。因此,有一些限制,例如無法附加至現有的處理序。CLR 程式碼剖析工具必須啟動程序以正確的環境變數集合。服務通常由啟動作業系統直接,像 ASP.NET,特殊程序必須後接 (從下載如需詳細資訊,請參閱 CLR Profiler.doc)。為一個另外,某些我們已加入的 API 支援的案例附加在.NET Framework 4 (但只 API 支援 ;附加 CLR 程式碼剖析工具工具的支援會遵循很快之後)。

CLR 程式碼剖析工具啟動應用程式時, 它會提供選項的蒐集的資訊類型。預設情況下,它取得回呼,每次受管理的方法呼叫或傳回一個的呼叫中以及每個 GC 堆積配置。通常稱為這些回呼,導致程式大幅降低,以及建立大型的記錄檔案超過 100 MB) 的許多真實世界的案例。CLR 程式碼剖析工具不會測量執行時間,並限制本身來追蹤的唯一的 GC 堆積配置的每個不同的方法被呼叫的次數。CLR 程式碼剖析工具會使用 method-callmethod-return 事件來追蹤何種方法是目前在堆疊,所以每個呼叫和堆積配置可以使用屬性以完整的呼叫堆疊的事件發生的位置。這會識別正在建立物件的位置。

不幸的是,預設的 CLR 分析工具收集詳細的資訊可以讓資料收集程序非常慢的速度 (分鐘)。然而,很少需要這項詳細的資訊。其中一個最有用的方法並不需要在一般執行期間無法收集任何其他資訊。雖然是一定要知道記憶體配置發生許多錯誤可以來修復只要瞭解維持作用的記憶體。若要回答這個後者的問題,一個不需要呼叫堆疊或配置事件 ;而是,簡單快照集 GC 堆積 (Heap) 的需要。因此,大部分調查的其中一個可以關閉所有的一般事件記錄,並取得良好的經驗 (合理分析效能,以及用來識別問題)。這是在這裡使用的技巧。

使用 CLR 程式碼剖析工具

如果您從 Web 下載 CLR 程式碼剖析工具,並解壓縮到預設位置,程式最後會 C:\CLR Profiler\Binaries\x86\ClrProfiler.exe (沒有一個 X64 版本,太)。啟動 CLR 程式碼剖析工具會顯示表單的圖 3] 所示。


圖 3 CLR 程式碼剖析工具啟動螢幕

此 [啟動] 對話方塊控制設定檔資料的集合。核取方塊表示執行應用程式時, 程式碼剖析會使用並配置和呼叫的資料將會收集 (記錄檔會詳細資訊)。由於我們是興趣只堆積快照,只在程式碼剖析作用中方塊內有保留選取 ([配置] 和 [電話] 方塊應該取消核取)。因此,CLR 分析工具將會記錄在程式執行,追蹤回應] 和 [向下的記錄檔大小的僅 Bare 的最小。

如果您想要的設定檔的應用程式並不需要任何命令列引數或特殊的啟動目錄,您可以僅選取起始應用程式,並 CLR 程式碼剖析工具會顯示檔案選擇器] 對話方塊來選取要執行應用程式。如果需要命令列參數或應用程式目前的目錄設定您應該將它們設使用檔案->之前按一下 [啟動應用程式上的 [設定 [參數] 功能表]。

取得堆積快照集

後選取 [應用程式可執行 CLR 程式碼剖析工具會立即開始執行應用程式。因為我們已經關閉配置和呼叫程式碼剖析,它應該執行非常接近到正常速度。CLR 分析工具現在貼附到的應用程式,且在任何時候您可以選取 [立即顯示堆積,並取得堆積快照集。堆積快照集 XMLView 應用程式就會發生看起來像的 圖 4] 會顯示內容。

這是整個 GC 堆積的表示 (顯示只即時物件)。當 GC 堆積實際的任意圖形 CLR 程式碼剖析工具決定連結和表單出它稱為根一個 pseudo-node 中根目錄的樹狀結構。每一個子系的大小可以然後被屬性至其父系,以便在根目錄,所有的即時物件有已達到的。在上述範例中 GC 堆積 (Heap) 必須 5.4 MB 的物件。這個 5.4 MB 不包含不已配置,在 GC 堆積中的可用空間,並因此,大保證為堆積 (Heap) 配置的虛擬記憶體數量 (確切的大小取決於,但如前一篇文章所提到 1.6 倍大是良好的估計)。

通常,沒有一個以上的單一路徑從根到特定的物件。CLR 程式碼剖析工具永遠會選擇最短路徑顯示在樹狀目錄中。但是,如果具有相同的長度的多個路徑選擇由任意。此外,預設情況下,CLR 程式碼剖析工具並不會顯示個別物件但會將所有的物件有相同的父型別和相同的子型別相同的節點的一部份。這項技巧通常會一個很好的摺疊連結的清單節點和藉此簡化檢視其他遞迴資料結構。如果此彙總不希望,您可以開啟它關閉上按一下滑鼠右鍵,選取顯示個別的執行個體。

堆積圖表視窗的頂端是兩個選項按鈕群組。第一個是 [配合表示在 [圖表] 檢視中的每個方塊的高度。第二個是決定要取得顯示的多少節點有的 [詳細資料] 按鈕。很有用,將它設定為很粗略,要保留圖形的整潔。通常,您感興趣只有圖形有東西要如何處理特定型別,只有物件相關的型別 XmlPositions 例如)。CLR 分析工具可讓您篩選這及其他準則 (按一下滑鼠右鍵->篩選條件)。

在的圖 4 XMLView 範例,大量的 GC 堆積 (5.1 MB) 會是 XmlView 型別的執行個體的子系。該 XmlView 實例會具有 XmlPositions 依次有為 5 MB XmlElementPositionInfo 陣列型別的子系。XmlElementPositionInfo 是會追蹤的起點在檔案中的每個的 XML 標記之大型陣列,它的大小是讀取 XML 檔案的大小比例 (因為這是一個非常大的檔案,預期的是這個陣列將會大)。因此,我們認為是合理的堆積大小。


圖 4 之前搜尋作業 CLR 程式碼剖析工具快照

追蹤 GC 堆積成長

在 XMLView 的範例我們問題不是 GC 堆積 (Heap),但的大幅成長已完成搜尋時的初始大小。因此,我們感興趣是兩個快照集之間堆積中的變更。CLR 程式碼剖析工具有只是用於進行調查這類的特殊功能。

佔領一個堆積快照之後, 我們可以再執行搜尋作業 (導致額外的記憶體配置),再採取其他的堆積快照集。結果會是下列的顯示:

(從 5.1 MB),與 XmlView 物件相關聯的記憶體現在已展開以 8.5 MB。節點有色彩兩種色彩更亮的紅色代表已存在於先前的快照集的記憶體。暗紅色是記憶體的唯一新的快照集。因此,新的記憶體已加入的型別稱為的 XmlFind (這不是意外),如示的 [圖 5。向右捲動透過,我們發現這個額外的記憶體的所有已 Int64 陣列,以示的圖 6 屬於型別 LineStream,相關聯。

這是識別 Bug 所需資訊!其實.NET XML 剖析器有能力以取得數字資料行的行號的 XML 標記,但不是它的檔案位置。若要解決這個問題,程式會需要一個行號對應到檔案位置。使用此 Int64 陣列。但是,程式碼這種對應只需用於 目前 XML 項目標籤的可能跨越任何數目的但通常只需要幾個的行。這種對應未重被設允許成長不必要地大對應陣列在尋找程式碼。此修正程式一般,但此錯誤就可能已存在永遠如果未執行 [記憶體] 稽核。使用 CLR 分析工具,花幾分鐘只尋找已修正此錯誤很重要的資訊。


圖 5 CLR 程式碼剖析工具快照集之後搜尋作業的第 1 篇


圖 6 CLR 程式碼剖析工具快照集之後搜尋作業的第 2 篇

顯示新的單獨物件

在先前的範例是相當容易以視覺化方式區別其色彩的最後一個快照集所特有的物件。但是,這很難執行的大型物件圖形,而且可能很容易完全,從圖表移除舊的物件可以完成使用 CLR 程式碼剖析工具。這項功能會將配置事件用於實作,所以之後取得第一個快照集,您要核取 [配置] 方塊 (可以讓 [呼叫方塊未核取)。下列這,互動應用程式,直到它配置更多的記憶體然後選取 [顯示堆積立即一次。它會和以前一樣顯示紅色的堆積使用其兩個色彩。現在上, 按一下滑鼠右鍵在 [] 視窗中,選取 [顯示新物件,新的視窗會出現,顯示您只有兩個快照集之間配置在 GC 堆積部份的功能。

追蹤所有的根物件

您可能想知道如何記憶體遺漏 (Memory Leak) 可以發生最後指定記憶體回收行程的 Managed 碼清除所有未使用的記憶體。但是,有情況下,您可能認為他們會被釋放之後,長項目可以保持作用。就例如非預期的參考可能會使管理的物件存留下來。

追蹤記憶體遺漏 (Memory Leak) 的技巧會很類似的了解但找到的記憶體耗用量。只有實際不同的是,但找到的記憶體耗用量,我們執行預期物件 (它就不應該為大) 的記憶體中而有裂縫,我們不要指望物件都成為 GC 堆積中。使用堆積快照集] 和 [顯示新的物件功能,很容易識別的物件不應該是作用 (使用您瞭解案例的)。如果物件在堆積中存留,必須要有某些物件的參照。但是,如果有一個以上的單一參考,您會發現移除一個連結仍無法解決問題因為另一個參考會保持作用。因此,將會尋找所有從根的路徑,讓特定的物件保持在作用 GC 堆積中方便。CLR 程式碼剖析工具有執行此動作的特殊支援。第一次,已選取 [引人興趣的要聞] 節點。然後按一下滑鼠右鍵,選取以顯示的參考和新的圖形會出現,顯示從根物件的所有路徑中有問題。這會為您提供的參考保留物件堆積中運作的足夠資訊來識別。排除物件連結,可以修正您的記憶體遺漏。

取得配置呼叫堆疊

檢視 GC 堆積 (Heap),就經常足夠用來診斷大部分的最高的記憶體耗用問題。但是上, 就有時候很有用瞭解呼叫堆疊配置的發生。若要取得該資訊,您必須執行程式之前,先選取配置] 核取方塊。這會造成 CLR 配置發生時,記錄堆疊追蹤的分析工具。收集此資訊會加大記錄檔,但它仍通常會是小於 50 MB 的中度複雜的應用程式。您不應該開啟撥號] 核取方塊上因為這會導致登入每個方法] 項目是詳細資訊 (很容易取得記錄檔容量大於 100 MB)。只有如果您使用 CLR 程式碼剖析工具來判斷哪一個方法呼叫通常,必要的方法呼叫資訊,而且這不是通常用於記憶體調查。

從堆積圖表,依序選取感興趣的節點上, 按一下滑鼠右鍵並選取 [顯示誰配置]。會顯示新的視窗顯示所有的呼叫堆疊配置該節點中的物件。您也可以從 [檢視] 的存取堆疊追蹤 >配置圖形] 功能表項目。這會顯示在屬性化至呼叫堆疊配置它們的程式中所做的所有配置。記憶體調查的這個檢視不是很有幫助因為許多這些配置的快速取得 GC 記憶體回收和因此,不會整體記憶體耗用量的暫時性物件。但是,因為 CPU 使用率 (要配置,然後再回收) 執行導致這些配置,此檢視時很有用您的應用程式是 CPU 繫結而 CPU 分析工具會顯示大量的 GC 中所花費的時間。精簡 GC 的時間最明顯的方法是執行較少的配置,這個檢視會顯示您配置的呼叫堆疊的位置。

在開發生命週期的一部份

此的文件中,我們受到透過常見的案例.NET GC 堆積的記憶體耗用量掌控其中的一個記憶體效能調查。使用可用和可下載的 CLR 分析工具,我們輕鬆地能夠取得快照集 GC 堆積 (Heap) 的應用程式中的不同點,並比較這些快照集與其他。CLR 程式碼剖析工具,使用它可以很容易執行的稽核 (它只需要幾分鐘),而且應該開發生命週期的每個應用程式的一部分。

Subramanian Ramaswamy 在 Microsoft 的 CLR 效能的程式管理員。他擁有一個博士在 [電子和電腦工程從喬治亞協會的技術。

Vance Morrison* 是在 Microsoft 的 CLR 效能夥伴架構設計人員和群組管理員。他主導的.NET 中繼語言設計,其開始後已經與.NET 相關。*