.NET垃圾收集器和x64虚拟内存

问题描述:

在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

这听起来像你没有保留对大数据的引用。垃圾收集器不会收集引用的对象。

+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

您是否检查过以确保您的分页文件已配置为可扩展至该大小?

更新

我一直在玩弄这个颇有几分与给定的例子,这里是我所看到的。

系统: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