C#:内存不足异常

C#:内存不足异常,c#,.net,out-of-memory,C#,.net,Out Of Memory,今天,我的应用程序抛出了一个OutOfMemoryException。对我来说,这几乎是不可能的,因为我有4GB的RAM和大量的虚拟内存。当我尝试将现有集合添加到新列表时发生错误 List<Vehicle> vList = new List<Vehicle>(selectedVehicles); List vList=新列表(所选车辆); 据我所知,这里没有分配多少内存,因为我的新列表中应该包含的车辆已经存在于内存中。我必须承认车辆是一个非常复杂的类,我试图立即向

今天,我的应用程序抛出了一个
OutOfMemoryException
。对我来说,这几乎是不可能的,因为我有4GB的RAM和大量的虚拟内存。当我尝试将现有集合添加到新列表时发生错误

List<Vehicle> vList = new List<Vehicle>(selectedVehicles);  
List vList=新列表(所选车辆);

据我所知,这里没有分配多少内存,因为我的新列表中应该包含的车辆已经存在于内存中。我必须承认
车辆
是一个非常复杂的类,我试图立即向新列表中添加大约50000项。但由于应用程序中的所有
Vehicle
s都来自一个只有200MB大小的数据库,我不知道此时会出现
OutOfMemoryException

数据库中存储的数据与应用程序中的内存非常不同

没有办法获得对象的确切大小,但您可以这样做:

GC.GetTotalMemory() 
加载一定数量的对象后,查看加载列表时内存的变化

如果是这个列表导致了过度的内存使用,那么我们可以寻找方法来最小化它。例如,为什么首先要将50000个对象同时加载到内存中。如果你需要的话,给DB打电话不是最好吗

如果您看看这里:您还会看到.NET中的对象比它们的实际数据大。通用列表比数组更像是一个内存占用器。如果对象中有一个通用列表,那么它将增长得更快。

两点:

  • 如果您运行的是32位Windows,则无法访问所有4GB,只能访问2GB
  • 不要忘记,
    List
    的底层实现是一个数组。如果内存严重碎片化,则可能没有足够的连续空间来分配
    列表
    ,即使总的来说您有足够的可用内存
  • OutOfMemoryException(在32位机器上)和内存的实际硬限制一样经常是关于碎片的-你会发现很多关于这一点,但这里是我在谷歌的第一次点击,简要地讨论它:。(@Anthony Pegram在上面的评论中提到了同样的问题)

    也就是说,上面的代码还有另一种可能性:当您对列表使用“IEnumerable”构造函数时,可能不给对象任何关于传递给列表构造函数的集合大小的提示。如果要传递的对象不是集合(不实现
    ICollection
    接口),那么在幕后,列表实现将需要增长几倍(或许多倍),每次都会留下一个太小的数组,需要进行垃圾收集。垃圾收集器可能无法足够快地到达这些丢弃的阵列,您将得到错误

    对此最简单的修复方法是使用
    List(int-capacity)
    构造函数告诉框架要分配什么样的支持数组大小(即使您只是估计和猜测“50000”),然后使用
    AddRange(IEnumerable collection)
    方法实际填充列表

    所以,如果我是对的,最简单的“修复”:替换

    List<Vehicle> vList = new List<Vehicle>(selectedVehicles);
    
    List vList=新列表(所选车辆);
    

    List vList=新列表(50000);
    v列表添加范围(所选车辆);
    
    所有其他的评论和答案仍然适用于总体设计决策,但这可能是一个快速解决方案


    注意(如下@Alex所述),只有当
    selectedVehicles
    不是一个ICollection时,这才是一个问题。

    您不应该尝试一次将所有列表都包含在内,数据库中元素的大小与其放入内存的大小不同。 如果您想要处理元素,您应该使用for-each循环,并利用实体框架延迟加载,这样您就不会一次将所有元素带入内存。
    如果要显示列表,请使用分页(.Skip()和.take())

    。Net4.5不再对对象设置2GB限制。 将此行添加到App.config

    <runtime>
        <gcAllowVeryLargeObjects enabled="true" />    
    </runtime>
    
    
    
    而且可以创建非常大的对象,而无需从MemoryException中获取OutOfMemoryException

    <runtime>
        <gcAllowVeryLargeObjects enabled="true" />    
    </runtime>
    

    请注意,它将只在x64操作系统上工作

    3年前的话题,但我找到了另一个可行的解决方案。
    如果您确定有足够的可用内存,运行64位操作系统,但仍有异常,请转到
    项目属性
    ->
    构建
    选项卡,并确保将
    x64
    设置为
    平台目标


    我的开发团队解决了这个问题:

    我们将以下生成后脚本添加到.exe项目中并再次编译,将目标设置为x86并增加1.5 gb,同时将x64平台目标使用3.2 gb增加内存。我们的应用程序是32位的

    相关URL:

    脚本:

    if exist "$(DevEnvDir)..\tools\vsvars32.bat" (
        call "$(DevEnvDir)..\tools\vsvars32.bat"
        editbin /largeaddressaware "$(TargetPath)"
    )
    

    我知道这是一个老问题,但由于没有一个答案提到大型对象堆,这可能对其他发现此问题的人有用

    NET中任何超过85000字节的内存分配都来自大对象堆(LOH),而不是普通的小对象堆。为什么这很重要?因为大对象堆没有被压缩。这意味着大型对象堆会变得支离破碎,根据我的经验,这不可避免地会导致内存不足错误

    在原始问题中,列表中有50000项。在内部,列表使用一个数组,假设32位需要50000 x 4bytes=200000字节(如果是64位,则为该值的两倍)。因此,内存分配来自大型对象堆

    那么你能做些什么呢

    如果您使用的是4.5.1之前的.net版本,那么您所能做的就是了解问题并尽量避免它。因此,在本例中,不需要有车辆列表
    System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
    
    int tries = 0;
    while (tries++ < 2)
    {
      try 
      {
        . . some large allocation . .
        return;
      }
      catch (System.OutOfMemoryException)
      {
        System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
        GC.Collect();
      }
    }