在仍有空闲VM空间的情况下抛出System.OutOfMemoryException

时间:2013-09-05 14:19:51

标签: c# .net memory-management out-of-memory

在询问之前,我会做一个小小的免责声明: 是的,我知道虚拟内存,物理内存和工作集之间的差异。以下所有数字均指虚拟内存。

情况如下: 我们有一个32位的C#应用​​程序,它可以导入x86 C ++库(具有大量的本机依赖性,因此目前无法迁移到x64)。 该应用程序通过非托管组件加载大型数据集,然后尝试显示它(报告)。

但是,当数据集特别大时,在向列表添加项目时会抛出OutOfMemory异常,如下面的代码所示:

这里不足为奇。然而,令人惊讶的是,该应用程序仍有大约280MB的免费虚拟机。

在调试纯粹的非托管应用程序(C ++)时,情况从未如此 - 只有在没有剩余空闲虚拟机的情况下,或者如果没有足够大小的空闲地址空间块时,才能实现bad_alloc。

因此,问题 - 这可能是什么原因?我理解如何解决这个问题 - 非托管组件确实会占用大量内存,并且会产生大量碎片 - 但是OutOfMemoryException出现这么早的原因是什么?

有问题的代码如下:

List<Cell> r = new List<Cell>(cols);
for (int j = 0; j < cols; j++)
{
    r.Add(new CustomCell()); // The exception is thrown on this line
}

在异常时刻,列表(一次出现)有85个项目,容量为200-something(列数,如构造函数中所示)。因此,异常最有可能发生在CustomCell分配中。 CustomCell对象有很多字段,但总共不到1KB。 280MB的可用内存位于64KB到14MB的块中 - 因此应该有足够的空间来分配它。

3 个答案:

答案 0 :(得分:2)

  

有大量高达14Mb的块。失败的操作是创建一个小于1KB的对象

14 MB绝对接近危险区域。 GC分配VM的方式与对象大小无关。 GC堆是从称为“段”的VM块创建的。程序启动时段大小为2 MB,但是当程序使用大量内存时,GC会动态增加新段的分配。显然你使用了大量的内存。您无法做任何事情来影响VM分配或避免VM地址空间碎片。

显然,您太接近32位进程的VM限制。您需要彻底修改代码,以便您可以少花钱。或者您需要在您的先决条件列表中放置64位操作系统。这可以为32位进程提供4千兆字节的VM地址空间。您需要额外的构建步骤才能利用它,如this answer中所述。

答案 1 :(得分:0)

修改

在添加代码之前编写。看起来你已经这样做了:

<击> List个实例具有容量。如果超出容量,则会调用不断增长的算法,使列表的容量加倍。

//decompiled from List<T>
private void EnsureCapacity(int min)
{
  if (this._items.Length >= min)
    return;
  int num = this._items.Length == 0 ? 4 : this._items.Length * 2;
  if ((uint) num > 2146435071U)
    num = 2146435071;
  if (num < min)
    num = min;
  this.Capacity = num; //causes a copying of src array to a new array
}

这可能导致意外的内存分配。如果您能够预测列表的最终大小,请事先分配并避免加倍:

var myList = new List<SomeType>(expectedCapacity)

<击>

答案 2 :(得分:0)

当你有64位应用程序时,你必须小心,最大数组大小为2 GB,检查你是否在一个数组中加载你的报告表。

更多信息: OutOfMemoryException on declaration of Large Array

这也可能适合你: http://social.msdn.microsoft.com/Forums/en-US/1a12abaa-50bd-4d28-b3c1-9de06a1488e9/how-to-create-an-extremely-large-arrayobject-2-gb-without-using-jagged-arrays-