2010-06-22 70 views
12

在Windows Server 2008 x64上運行帶有16GB內存的.NET應用程序。此應用程序需要獲取並分析大量數據(大約64GB),並將其全部保存在內存中。.NET垃圾收集器和x64虛擬內存

我期望看到的:進程大小從16GB擴展到64GB。 Windows使用虛擬內存根據需要將額外的數據分頁到磁盤或從磁盤分頁。這是經典的虛擬內存使用案例。

我實際看到的:進程大小限於物理內存量(16GB)。應用程序花費99.8%的時間在垃圾回收器中。

爲什麼我們的應用程序無法使用虛擬內存?這是配置.NET垃圾回收器還是Windows x64虛擬內存管理器本身的問題?我可以做些什麼來讓我們的應用程序使用虛擬內存,而不是僅限於物理內存?

謝謝。

- 布賴恩

更新:我已經寫了一個很小的程序表現出相同的行爲:

using System; 

namespace GCTest 
{ 
    class Program 
    { 
     static void Main() 
     { 
      byte[][] arrays = new byte[100000000][]; 
      for (int i = 0; i < arrays.Length; ++i) 
      { 
       arrays[i] = new byte[320]; 
       if (i % 100000 == 0) 
       { 
        Console.WriteLine("{0} arrays allocated", i); 
        System.Threading.Thread.Sleep(100); 
       } 
      } 
     } 
    } 
} 

如果你想嘗試一下,一定要建立針對x64。您可能需要修改一些常量來強調系統。我看到的行爲是當進程達到16GB的大小時,進程陷入了困境。沒有錯誤消息或拋出的異常。性能監視器報告GC中的CPU時間百分比接近100%。

這不是不可接受嗎?虛擬內存系統在哪裏?

+0

你用什麼來確定過程的大小? (讓我們先刪除簡單的選項:)) – Paolo 2010-06-22 14:40:10

+0

Windows任務管理器中的「提交大小」。性能監視器中還使用「#總提交字節數」。我很確定我正在測量進程的虛擬內存大小,而不是物理工作集。 – brianberns 2010-06-22 16:03:33

+0

我可以問明顯嗎?你是否真的需要立即將所有這些加載到內存中?是否有可能採取另一種方法並自行進行某種手動分頁? – CodingGorilla 2010-06-22 21:58:20

回答

2

這聽起來像你沒有保留對大數據的引用。垃圾收集器不會收集引用的對象。

+0

我不認爲這是相關的。沒有任何數據實際上是垃圾 - 它們都被引用。因此,我不希望GC收集任何內存。 似乎GC正在消耗100%的CPU,因爲進程耗盡了物理內存,而.NET正試圖釋放一些內存。然而,沒有什麼可以被釋放的 - 理想情況下,這個過程應該使用虛擬內存進行擴展,而不是花費所有時間在GC中嘗試釋放(不存在的)未使用的對象。 – brianberns 2010-06-22 16:06:20

+0

如果您要分配64 GB的數據,但實際只分配了16 GB,那麼其餘部分未被引用並且已被GC化。除非我不理解你的問題。 – 2010-06-22 16:21:20

+0

你不理解這個問題。我試圖分配64GB的數據,但是在我只分配了16GB的數據後,這個過程陷入了困境。我從來沒有機會分配其餘的數據。 – brianberns 2010-06-22 16:50:31

10

您是否檢查過以確保您的分頁文件已配置爲可擴展至該大小?

更新

我一直在玩弄這個頗有幾分與給定的例子,這裏是我所看到的。

系統:Windows 7 64bit,6GB三通道RAM,8核。

  1. 您需要在您的操作系統上安裝另一個主軸上的附加頁面文件,或者這種調查將軟管您的機器。如果一切都在爭奪同一個分頁文件,它會讓事情變得更糟。

  2. 我看到大量數據在GC中被一代一代地提升,加上大量的GC掃描\集合,以及由於達到物理內存限制而導致的大量頁面錯誤。我只能假設,當物理內存耗盡\非常高時,這會觸發生成掃描和促銷活動,從而導致大量的頁面釋放內存被觸摸,這導致死亡Spriral,因爲觸摸的內存被分頁和其他內存被迫離開。整個事情以一團糟的混亂結束。這在分配大量以小對象堆結尾的長壽命對象時似乎是不可避免的。

現在比較這對在一個時尚分配對象將直接分配他們到大對象堆(不遭受同樣的清掃和推廣方面的問題):

private static void Main() 
{ 
    const int MaxNodeCount = 100000000; 
    const int LargeObjectSize = (85 * 1000); 

    LinkedList<byte[]> list = new LinkedList<byte[]>(); 

    for (long i = 0; i < MaxNodeCount; ++i) 
    { 
     list.AddLast(new byte[LargeObjectSize]); 

     if (i % 100000 == 0) 
     { 
      Console.WriteLine("{0:N0} 'approx' extra bytes allocated.", 
       ((i + 1) * LargeObjectSize)); 
     } 
    } 
} 

可正常工作即虛擬內存被使用,然後最終耗盡 - 54GB在我的環境\配置。

因此,分配大量長壽命的小物體最終會導致GC中的惡性循環,因爲當物理內存耗盡時會產生掃描和促銷 - 這是頁面文件的死亡螺旋。

更新2

雖然調查我的一些選項\配置這使得沒有明顯的差異所發揮的問題:

  • 強制服務器GC模式。
  • 配置低延遲GC。
  • 強制GC嘗試分攤GC的各種組合。
  • Min \ Max過程工作集。
+0

不,但是Windows虛擬內存管理器不應該自動啓動分頁,至少在某種程度上超過16GB?如果虛擬內存受到物理內存的限制,有什麼意義? – brianberns 2010-06-22 16:08:02

+0

它是可配置的 - 值得檢查。問題可能出自一個意想不到的政策,也可能是一些明亮的火花將尋呼關閉。 – 2010-06-22 16:08:49

+0

好的,我增加了30GB的頁面文件,但它沒有效果。我認爲.NET垃圾收集器阻止我分配超過16GB的對象,所以Windows虛擬內存系統從來沒有機會開始分頁。 – brianberns 2010-06-22 17:41:18