C# System.OutOfMemoryException在仍有可用VM空间时引发

C# System.OutOfMemoryException在仍有可用VM空间时引发,c#,.net,memory-management,out-of-memory,C#,.net,Memory Management,Out Of Memory,在提问之前,我先做一个小小的免责声明: 是的,我知道虚拟内存、物理内存和工作集之间的区别。以下所有数字均指虚拟内存 情况如下: 我们有一个32位的C++应用程序,它导入x86 C++库(有很多本地依赖关系,所以现在迁移到x64不是一个选项)。 应用程序通过非托管组件加载一个大型数据集,然后尝试显示它(报告) 但是,当数据集特别大时,在向列表中添加项时会引发OutOfMemory异常,如下面的代码所示: 这并不奇怪。然而,令人惊讶的是,应用程序仍然有大约280MB的空闲VM 在调试纯非托管应用程序

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

情况如下: 我们有一个32位的C++应用程序,它导入x86 C++库(有很多本地依赖关系,所以现在迁移到x64不是一个选项)。 应用程序通过非托管组件加载一个大型数据集,然后尝试显示它(报告)

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

这并不奇怪。然而,令人惊讶的是,应用程序仍然有大约280MB的空闲VM

在调试纯非托管应用程序(C++)时,情况并非如此——只有在没有剩余的空闲VM,或者没有足够大小的空闲地址空间块的情况下,才能实现坏的分配

因此,问题是——这可能是什么原因?我知道如何解决这个问题——非托管组件确实消耗了大量内存,并且产生了大量碎片——但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
}
List r=新列表(cols);
对于(int j=0;j
在异常时刻,列表(在一次事件中)有85个项,其容量为200多个(列数,如构造函数中所示)。因此,异常很可能发生在CustomCell分配中。CustomCell对象有许多字段,但总容量肯定小于1KB。280MB的可用内存位于64KB到14MB的块中,因此应该有足够的空间来分配它。

EDIT

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

列表
实例具有容量。如果超出容量,将调用一个增长算法,使列表的容量加倍

//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
}
//从列表中反编译
私人无效保证资本(整数最小值)
{
如果(此._items.Length>=min)
返回;
int num=this.\u items.Length==0?4:this.\u items.Length*2;
如果((uint)数量>2146435071U)
num=2146435071;
if(num
这可能会导致意外的内存分配。如果您能够预测列表的最终大小,请提前分配并避免加倍:

var myList = new List<SomeType>(expectedCapacity)
var myList=新列表(预期容量)

当您使用64位应用程序时,您还必须小心,最大数组大小为2 GB。请检查是否正在数组中加载报表表

更多信息:

这也可能是为您设置的:

有高达14Mb的块可用。失败的操作正在创建总计小于1KB的对象

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


显然,您太接近32位进程的VM限制了。您需要彻底修改您的代码,这样您就可以用更少的代码勉强度日。或者,您需要将64位操作系统放在先决条件列表中。它可以为32位进程提供4G的VM地址空间。您需要一个额外的构建步骤来利用它,如中所述。

可能问题在于内存碎片。您有足够的可用内存,但缺少足够大的连续块来存储新数据。当然不是这样。我已经使用Sysinternals和visualstudio中的VMMap实用程序研究了应用程序内存-有高达14Mb的可用块。失败的操作正在创建一个总容量小于1KB的对象。
OutOfMemory异常在向列表中添加项目时引发。
此处的列表是什么?使用代码段更新了问题。好了,这很接近,但没有解释它。正如我所说的,我有许多大小不同的块—总共280 MB—有足够的空间来放置2MB块。您确定段大小不是16MB吗?如果是的话,这就可以解释为什么你一定错过了答案中的“动态增长分配”部分。一开始只有2MB。当然,它可以增长到16MB。您已经错过了280MB的部分:)您能描述一下发生这种情况的确切情况吗?好的,我在别处找到了更详细的解释。你在高层还是对的,所以我接受你的答案。