改善記憶體回收效能

使用 C# 和 Visual Basic 撰寫的通用 Windows 平台 (UWP) 應用程式會透過 .NET 記憶體回收行程自動管理記憶體。 本節會針對 UWP 應用程式中的 .NET 記憶體回收行程摘要說明其行為和效能最佳做法。 若想進一步瞭解 .NET 記憶體回收行程的運作方式,以及用於偵錯和分析記憶體回收行程效能的工具,請參閱記憶體回收 (機器翻譯)。

注意 當應用程式發生一般記憶體問題時,很可能需要介入記憶體回收行程的預設行為。 如需詳細資訊,請參閱在 Visual Studio 2015 中偵錯時使用的記憶體使用量工具 (英文)。 本主題僅適用於 C# 與 Visual Basic。

 

記憶體回收行程會藉由平衡受控堆積的記憶體耗用量與記憶體回收作業需要執行的工作量,判斷執行時機。 記憶體回收行程採用的其中一種方式,是將堆積分成幾個層代,大部分時間只會收集此堆積的一部分。 受控堆積中有三個層代:

  • 層代 0。 這個層代含有不到 85 KB 的新配置物件。85 KB 或更大的物件會形成大型物件堆積, 由層代 2 回收作業加以回收。 層代 0 回收作業是最常發生的回收類型,會清除區域變數等短期物件。
  • 層代 1。 這個層代含有從層代 0 回收作業中殘留的物件, 是層代 0 與層代 2 之間的緩衝區。 層代 1 回收作業的發生頻率低於層代 0 回收作業,會清除在先前的層代 0 回收作業期間的作用中暫存物件。 層代 1 回收作業也會回收層代 0。
  • 層代 2。 這個層代含有從層代 0 與層代 1 回收作業中殘留的長期物件。 層代 2 回收作業最少發生,會回收全體受控堆積,包括含有 85 KB 或更大物件的大型物件堆積。

您可以從 2 個層面測量記憶體回收行程的效能:執行記憶體回收作業所需的時間,以及受控堆積的記憶體耗用量。 如果您有堆積大小少於 100 MB 的小型應用程式,請專注於減少記憶體耗用量。 如果您有受控堆積大於 100 MB 的應用程式,則只要專注於減少記憶體回收時間。 以下說明如何協助 .NET 記憶體回收行程提升效能。

減少記憶體耗用量

釋放參考

應用程式中的物件參考可防止該物件及其參考的所有物件被回收。 .NET 編譯器能夠準確偵測變數不再使用的時機,讓該變數所保留的物件能夠納入回收作業。 不過,在部分情況下可能無法明顯判斷某些物件具有其他物件的參考,因為部分物件圖形的擁有者可能是您應用程式使用的程式庫。 若想瞭解可以使用哪些工具和技術找出從記憶體回收作業中殘留的物件,請參閱記憶體回收及效能 (機器翻譯)。

在必要時引發記憶體回收作業

唯有在您已測量過應用程式效能,並且判斷引發回收作業可以改善效能的情況下,才建議引發記憶體回收作業。

您可以透過呼叫 GC.Collect(n) (機器翻譯),引發層代的記憶體回收作業;此方法中的 n 為您想要回收的層代 (0、1 或 2)。

注意 建議您不要在應用程式中強制執行記憶體回收作業,因為記憶體回收行程使用許多啟發學習法來判斷執行回收的最佳時機,在許多情況下,強制回收會造成不必要的 CPU 使用。 不過,如果您知道應用程式中有大量不再使用的物件,而且想將此記憶體傳回系統,那麼或許可以強制執行記憶體回收作業。 舉例來說,您可以在遊戲的載入序列結尾引發回收作業,以便在遊戲開始之前釋放記憶體。   為了避免不慎引發太多次記憶體回收作業,您可以將 GCCollectionMode (機器翻譯) 設定為 Optimized, 指示記憶體回收行程只在判斷回收作業能有足夠合理成效時才啟動回收。

減少記憶體回收時間

若您已分析過您的應用程式,並觀察到大量記憶體回收時間,請參考本節內容。 與記憶體回收作業相關的暫停時間包括:執行單一記憶體回收階段所需的時間,以及應用程式花費在執行記憶體回收作業上的總計時間。 執行回收作業所需的時間取決於回收行程需要分析的即時資料量。 層代 0 和層代 1 都繫結至大小,但層代 2 會隨著應用程式內的作用中長期物件增加而持續變大; 換句話說,層代 0 和層代 1 的回收時間都受到限定,而層代 2 的回收作業則需要較長時間。 記憶體回收作業的執行頻率主要取決於您配置的記憶體量,因為記憶體回收作業會釋放記憶體以滿足配置要求。

記憶體回收行程偶爾會暫停您的應用程式以執行工作,但不一定會在應用程式執行回收作業的整個過程中暫停您的應用程式。 使用者通常不會在您的應用程式中感知到暫停時間,尤其是層代 0 和層代 1 的回收作業。 .NET 記憶體回收行程的背景記憶體回收 (機器翻譯) 功能可在應用程式執行期間同時執行層代 2 回收作業,而且只會在短時間內暫停您的應用程式。 然而,層代 2 回收作業不一定可以當作背景回收作業執行; 在這種情況下,如果您的堆積夠大 (超過 100 MB),使用者便能感知到暫停。

頻繁執行記憶體回收作業可能會導致 CPU 耗用量增加 (因而使功率耗用量也增加)、載入時間較長,或者應用程式中畫面播放速率降低。 以下會介紹一些技巧,協助您減少記憶體回收時間,以及受控 UWP 應用程式中與回收作業相關的暫停。

減少記憶體配置

如果您沒有配置任何物件,那麼除非系統中發生記憶體不足的狀況,否則記憶體回收行程不會執行。 減少配置的記憶體量,能直接降低記憶體行程的執行頻率。

如果您希望應用程式的某些區段完全不要出現暫停,可以事先在效能較不重要的時段預先配置必要的物件。 舉例來說,遊戲可能會在載入等級畫面時配置遊戲體驗需要的所有物件,而不在遊戲期間進行任何配置。 這種做法可避免在使用者玩遊戲時發生暫停,並使畫面播放速率更高、更穩定。

避免中等長度生命週期的物件以減少層代 2 回收作業

您的應用程式中有非常短期和/或非常長期的物件時,層代記憶體回收作業的效能最高。 成本較低的的層代 0 和層代 1 回收作業會回收短期物件,長期物件則會升級至回收頻率較低的層代 2。 長期物件是指在應用程式整體期間或應用程式的重大期間 (例如在特定頁面或遊戲等級期間) 使用的物件。

如果您經常建立生命週期短暫但長度足以升級至層代 2 的物件,就會出現更多成本高昂的層代 2 回收作業。 您或許可以透過加速回收現有物件或釋放物件,減少層代 2 的回收作業。

常見的中等長度生命週期物件範例,是用於在使用者捲動清單時顯示項目的物件。 若在物件在清單項目捲動進入檢視時建立,而且在清單項目捲動出檢視後不再受到參考,那麼您的應用程式通常會有大量的層代 2 回收作業。 在這種情況下,您可以針對向使用者主動顯示的資料預先配置及重複使用一組物件,並使用短期物件在清單項目進入檢視時載入資訊。

避免生命週期短的大型物件以減少層代 2 回收作業

所有 85 KB 或更大的物件都會配置在大型物件堆積 (LOH),由層代 2 回收。 如果您有大於 85 KB 的暫存變數 (如緩衝區),層代 2 代回收作業會加以清除。 將暫存變數限制為小於 85 KB,可減少應用程式中的層代 2 代回收作業數。 一個常見的技巧是建立緩衝集區,並重複使用集區中的物件,以免出現大型暫時配置。

避免有大量參考的物件

記憶體回收行程會根據物件之間的參考 (從應用程式中的根目錄開始),判斷哪些是即時物件。 如需詳細資訊,請參閱記憶體回收過程中會發生的事 (機器翻譯)。 如果物件包含許多參考,記憶體回收行程就有更多工作要做。 一個常見的技術 (通常用於大型物件) 是將大量參考的物件轉換為沒有參考的物件 (例如,不儲存參考,改為儲存索引)。 當然,這個技巧只適用於邏輯上可行的情況。

將物件參考替換成索引可能會對應用程式帶來具干擾性的複雜變更,對具有大量參考的大型物件而言最有效果。 唯有在您發現應用程式中有大量記憶體回收時間與參考眾多的物件相關時,才能使用這個技巧。