在Windows Server 2008 x64上运行带有16GB RAM的.NET应用程序。此应用程序需要获取和分析大量数据(大约64GB),并将其全部保存在内存中。
我期望看到的:流程大小扩展到16GB到64GB。 Windows根据需要使用虚拟内存将额外数据分页到磁盘或从磁盘分页。这是经典的虚拟内存使用案例。
我实际看到的内容:进程大小仅限于物理内存量(16GB)。应用程序将99.8%的时间花在垃圾收集器上。
为什么我们的应用程序无法使用虚拟内存?这是.NET垃圾收集器配置中的问题,还是Windows x64虚拟内存管理器本身的问题?我该怎么做才能让我们的应用程序使用虚拟内存而不仅限于物理内存?
感谢。
- Brian
更新:我写了一个非常小的程序,表现出相同的行为:
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 :(得分:10)
您是否检查过以确保您的页面文件已配置为可以扩展到该大小?
<强>更新强>
我用你的例子一直在玩这个,这就是我所看到的。
系统:Windows 7 64位,6GB三通道RAM,8核。
您需要在操作系统的另一个主轴上添加一个页面文件,否则这种调查会给您的机器带来麻烦。如果所有内容都在同一页面文件中进行,那就会让事情变得更糟。
我看到GC中代代相传的大量数据,以及大量的GC扫描\集合,以及因达到物理内存限制而导致的大量页面错误。我只能假设当物理内存耗尽\非常高时,这会触发生成扫描和促销,从而导致大量的页面调出内存被触摸,这会导致触发内存被分页和其他内存中的死亡事件被迫出局。整个事情以一个潮湿的混乱结束。当分配大量长寿命对象时,这似乎是不可避免的,这些对象最终会出现在小对象堆中。
现在比较一下,以一种方式分配对象会将它们直接分配到大对象堆(它没有遭受同样的清扫和促销问题):
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
在调查这个问题的同时,我使用了许多选项\配置,但没有产生明显的差异:
答案 1 :(得分:2)
听起来你没有保留对大数据的引用。垃圾收集器不会收集引用的对象。