2009-06-24 17 views
8

在談到關於採用了在啓動時近1.5G內存的應用程序的特定組的同事......他向我指出一個非常好的鏈接.NET production debugging如何從多個託管應用程序的大對象堆LOH中獲取未使用的內存?

有我疑惑的是該部分...

例如,如果您將1 MB的 內存分配給單個塊,則大對象堆將擴展爲1 MB大小。 釋放此對象時,大型對象堆 不會解除虛擬內存的 解析,因此堆保持大小爲1 MB。如果您稍後分配了另一個 500 KB塊,則新塊將在 內存的1 MB塊內分配的 屬於大對象 堆。在過程生命週期中,大對象堆始終增長爲保留 當前引用的所有大塊分配 ,但從未 在釋放對象時收縮,即使發生垃圾回收也不會收縮 。 下一頁的圖2.4顯示了一個大對象堆的示例 。

現在我們假設我們有一個虛構的應用程序,它會創建一系列大對象(大於85KB),所以大對象堆增長可以說爲200兆。現在讓我們說,我們有10個這樣的應用程序實例正在運行..以便分配2000個Megs。現在是這個內存永遠不會回到操作系統,直到過程關閉...(是我所理解的)

我的理解有沒有差距?我們如何在各種LOHeap中找回未使用的記憶;我們不會創建OutOfMemoryExceptions的完美風暴嗎?

更新:從Marc的迴應中,我想澄清一下,LOH對象沒有被引用 - 大對象是use-n-throw - 但是即使堆相對空初始激增。

更新#2:就包括代碼段(誇張,但得到的點對面,我認爲)。我看到身邊的時候,一個OutOfMemoryException虛擬內存擊中1.5G標記我的機器(1.7G上另一)..從Eric L.'s blog post,'進程內存可視化爲磁盤上的海量文件..' - 這個結果是意料之外的。在這種情況下,機器在硬盤上有可用空間的GB。 PageFile.sys操作系統文件(或相關設置)是否施加任何限制?

 static float _megaBytes; 
     static readonly int BYTES_IN_MB = 1024*1024; 

     static void BigBite() 
     { 
      try 
      { 
       var list = new List<byte[]>(); 
       int i = 1; 

       for (int x = 0; x < 1500; x++) 
       { 
       var memory = new byte[BYTES_IN_MB + i]; 
       _megaBytes += memory.Length/BYTES_IN_MB; 
       list.Add(memory); 
       Console.WriteLine("Allocation #{0} : {1}MB now", i++, _megaBytes); 
       } 
      } 
      catch (Exception e) 
      { Console.WriteLine("Boom! {0}", e); // I put a breakpoint here to check the console 
       throw; 
      } 
     } 
     static void Main(string[] args) 
     { 
      BigBite(); 
      Console.WriteLine("Check VM now!"); Console.ReadLine(); 
      _megaBytes = 0; 

      ThreadPool.QueueUserWorkItem(delegate { BigBite(); }); 
      ThreadPool.QueueUserWorkItem(delegate { BigBite(); }); 
      Console.ReadLine(); // will blow before it reaches here 
     } 
+0

(回覆評論) – 2009-06-24 10:11:58

回答

2

好吧,如果你真的有這種分配模式,你可以將你的大對象到另一個應用程序域 - 當你決定要釋放所有的大型物體,釋放的AppDomain併爲應用程序域堆會被髮布。

+0

LOH是不是每個AppDomain。 – mfawzymkh 2009-06-25 21:48:43

3

如果LOH想要保留內存,這是由LOH決定的 - 但是,請不要忘記OutOfMemoryException是每個進程,因爲真正的硬盤是虛擬內存的限制因素。最近Eric Lippert blogged about this。當然,這並不妨礙它從所有尋呼中獲得糟糕的性能......

+1

將會更新這個問題,在這種情況下,LOH對象本身就是使用n-throw。一旦他們達到了目的,LOH對象就被釋放,但堆不縮小。考慮一個組件在將對象序列化到管道中之前進行一些序列化操作:在這種情況下,您將200 Megs分配爲字節數組,這在數據傳輸後是垃圾。 – Gishu 2009-06-24 09:47:46

5

有一個我想先澄清的說明。 - 假設您將應用程序作爲32位應用程序運行,如果您啓用了大型地址空間切換,則可用於您的進程的VA空間僅爲2 GB,3 GB,因此即使您擁有巨大頁面文件,也無所謂32位進程,如果你運行64位,那你有很大的地址空間。

與大小> 85000字節
  • 對象是在LOH分配,值得注意的是85000字節沒有85K,這也是實現細節可能會改變。 現在,回到你的問題。 GC將取消在兩種情況下未使用的LOH段 1-當機器上的存儲壓力較高(〜95-98%)時012-2-當它無法滿足新的分配請求時,解除LOH中未使用的頁面

因此,您將在其中一種情況下取​​回內存。 事實上,在達到2GB限制之前擊中OOM可能意味着您有VA碎片,當您沒有連續的VA地址空間來滿足新分配時會發生VA碎片,例如,您要求8KB碎片,並且您不要在您的VA中沒有連續2頁(假設頁面大小爲4 K)

您可以在Windows調試工具中使用!vamap調試器擴展來驗證此操作。

希望這有助於 感謝

相關問題