Dot-Net

OutOfMemory,但許多對像沒有 gcroots

  • October 27, 2009

我們正在開發一個相當大的 Windows 窗體應用程序。在幾個客戶的電腦中,它經常因 OutOfMemory 異常而崩潰。在異常發生後(從 UnhandledException 處理程序呼叫 clrdump)獲得應用程序的完整記憶體轉儲後,我使用“.NET Memory Profiler”和 windbg 對其進行了分析。

Memory Profiler 在活動對象實例中僅顯示 130MB。有趣的是,對於許多對像類型,已經顯示了非常大量的不可達實例(例如 22000 個不可達字節

$$ $$實例)。在本機記憶體統計中,所有數據堆中的數據總計為 127MB(這還可以),但表示第 2 代堆中的 133MB 和大堆中的 640MB 無法訪問(不好!)。 使用 windbg 分析轉儲時,確認了上述統計資訊:

!dumpheap -stat
..... acceptable object sizes...
79330a00   467216     30638712 System.String
0016d488     4804    221756612      Free
79333470    27089    574278304 System.Byte[]

應用程序在執行時確實使用了大量的短緩衝區,但不會洩漏它們。測試許多字節

$$ $$帶有 !gcroot 的實例最終沒有根。顯然,如記憶體分析器所示,這些數組中的大多數都是不可訪問的。 只是為了確保一切正常,!finalizequeue 顯示沒有對象正在等待完成

generation 0 has 138 finalizable objects (18bd1938->18bd1b60)
generation 1 has 182 finalizable objects (18bd1660->18bd1938)
generation 2 has 75372 finalizable objects (18b87cb0->18bd1660)
Ready for finalization 0 objects (18bd1b60->18bd1b60)

並且還檢查本機終結器執行緒堆棧跟踪是否顯示它沒有被阻止。

目前,我不知道如何診斷 GC 不收集數據的原因(我相信它會很樂意這樣做,因為程序記憶體不足..)

**編輯:**根據下面的輸入,我閱讀了更多關於大型對象堆碎片的資訊,似乎情況可能如此。

我已經看到一些建議為這種數據分配更大的記憶體塊(各種字節

$$ $$在我的情況下)並自己管理該區域的記憶體,但這似乎是一個相當老套的解決方案,而不是我希望解決不那麼特殊的桌面應用程序問題的解決方案。 碎片問題是由於 LOH 上的對像在存在期間沒有重新定位這一事實(至少這是許多 Microsoft 的部落格中所說的)造成的,這是可以理解的,但一旦達到一定的記憶體壓力似乎是合乎邏輯的,例如面臨 OOM 的威脅,應執行重定位。

在完全相信碎片是原因之前唯一讓我擔心的是,LOH 上的這麼多對像沒有 gcroot 引用 - 這是因為即使對於 LOH 垃圾收集也只是部分執行?

我會很高興為我指出任何有趣的解決方案,因為目前我所知道的唯一一個是一些預分配記憶體塊的自定義管理。

歡迎任何想法。謝謝。

和往常一樣,事實證明並沒有什麼不同。我們發現了一個案例,其中應用程序確實消耗了大量記憶體並最終會出現 OOM。在我們發現之前我們得到的轉儲中有什麼奇怪的是有很多沒有 gcroot 的對象 - 我不明白為什麼它沒有被釋放並用於新的分配?然後我想到,當 OOM 發生時可能發生了什麼 - 堆棧已展開,擁有記憶體的對像不再可訪問,然後執行了轉儲。這就是為什麼似乎有很多記憶體可以被 GC 處理的原因。

我在調試版本中所做的 - 檢索真實的記憶體狀態轉儲 - 是創建一個 Threading.Timer 來檢查是否可以分配一些相當大的對象 - 如果無法分配,這是一個指示我們在 OOM 附近,現在是進行記憶體轉儲的好時機。程式碼如下:

private static void OomWatchDog(object obj)
{
try                          
{
  using(System.Runtime.MemoryFailPoint memFailPoint = 
         new System.Runtime.MemoryFailPoint(20))
  {
  }
}
catch (InsufficientMemoryException)
{
  PerformDump();
}
}

LOH 會出現碎片化。本文提供了分析和解決它的基本方向。

也許您可以發布一些程式碼,顯示這些字節的“典型”用法

$$ $$緩衝區?

引用自:https://stackoverflow.com/questions/1549252