C#:内存不足异常


71

今天我的申请提出了OutOfMemoryException。对我来说,这几乎几乎是不可能的,因为我也有4GB RAM和很多虚拟内存。当我尝试将现有集合添加到新列表时发生了错误。

List<Vehicle> vList = new List<Vehicle>(selectedVehicles);  

据我了解,这里没有分配多少内存,因为我的新列表中应该包含的车辆已经存在于内存中。我不得不承认这Vehicle是一个非常复杂的类,我试图一次将大约50.000个项目添加到新列表中。但是,由于Vehicle应用程序中的所有内容都来自大小仅为200MB的数据库,因此我不知道这可能导致什么OutOfMemoryException


7
什么是值(和类型)selectedVehicles
哈罗德

2
OutOfMemoryException被抛出,你附加到进程使用调试器,看看问题可能是什么?多大对象?.NET Framework的对象大小硬限制为2 GB,减去框架本身消耗的开销。
科迪·格雷

1
Vehicle可能是结构而不是类吗?
Anthony Pegram

2
转换为.net对象时,200MB的数据库空间可能很容易占用两倍以上的空间。之后,它的占用空间可能会更小,但是框架试图立即获取不可用的大块连续内存。
StingyJack 2011年

1
关于您拥有多少内存以及系统允许拥有的虚拟内存数量的陈述表明,您对虚拟内存和物理内存的工作方式缺乏了解。您可能需要阅读有关该主题的一些信息。
安全猎犬

Answers:


80

两点:

  1. 如果您运行的是32位Windows,则将无法访问所有4GB内存,而只能访问2GB。
  2. 不要忘记的底层实现List是一个数组。如果您的内存非常分散,则List即使您总共有足够的可用内存,也可能没有足够的连续空间来分配您的。

25
在64位Windows中,您也只有2 GB可用空间。这是.NET Framework的局限性,而不仅仅是32位地址空间。
科迪·格雷

25
@CodyGray每个对象(数组)2GB,而不是2GB。
Henk Holterman

5
@CodyGray,我认为2GB阵列限制不在新的.NET版本中。
evolvedmicrobe

3
可以使用gcAllowVeryLargeObjects配置设置克服它。此处有更多详细信息-msdn.microsoft.com/en-us/library/…–
RBT

91

3岁的话题,但我找到了另一个可行的解决方案。如果您确定有足够的可用内存,正在运行64位操作系统并且仍然出现异常,请转到Project properties->Build选项卡,并确保将其设置x64Platform target

在此处输入图片说明


2
我看不到图片,有什么选择?
M.kazem Akhgary

3
Project properties- >Build标签- >变化Platform target,以x64
一个“”

71

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

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

并且可以创建非常大的对象而不会出现OutOfMemoryException

请注意,它仅适用于x64 OS!


2
做得很好。这对我有用,注意到必须将Build目标更改为x64。
Michael Blankenship 2015年

也适用于ASP.NET 4.5吗?使用本地报告,我的App v1(asp.net 3.5-clr 2.0-经典)可以正常运行,但我的App v2(asp.net 4.5,clr 4.0,经典)在同一台IIS服务器中生成OutOfMemoryException错误
Kiquenet

通过设置将内存设置为4.6G时出现异常
yu yang Jian

14

与应用程序中的内存相比,数据库中存储的数据有很大的不同。

无法获得对象的确切大小,但是您可以这样做:

GC.GetTotalMemory() 

装入一定数量的对象后,查看加载列表时内存的变化量。

如果是导致过多内存使用的列表,那么我们可以考虑将其最小化的方法。例如,为什么首先要一次将50,000个对象全部加载到内存中。按照您的要求调用数据库不是最好吗?

如果您在此处查看:http : //www.dotnetperls.com/array-memory,您还将看到.NET中的对象大于其实际数据。通用列表甚至比数组更像是内存消耗。如果您的对象中有一个通用列表,则它将更快地增长。


8

(在32位计算机上)OutOfMemoryException与碎片实际发生在内存上的实际硬限制一样多-您会发现很多有关此的信息,但这是我第一次在Google上进行的简短讨论:http : //blogs.msdn.com/b /joshwil/archive/2005/08/10/450202.aspx。(@Anthony Pegram在上面的评论中提到了相同的问题)。

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

最简单的解决方法是使用List(int capacity)构造函数告诉框架要分配的后备数组大小(即使您估计并仅猜测为“ 50000”),然后使用该AddRange(IEnumerable collection)方法实际填充列表。

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

List<Vehicle> vList = new List<Vehicle>(selectedVehicles);

List<Vehicle> vList = new List<Vehicle>(50000);  
vList.AddRange(selectedVehicles);

其他所有评论和答案仍适用于总体设计决策-但这 可能是一个快速解决方案。

注意(如下面的@Alex所述),这仅selectedVehicles是不是ICollection的问题。


3
如果selecyedVehicles是一个集合,则构造函数将分配正确的数组大小。无需通过AddRange。
Alex

5

我的开发团队解决了这种情况:

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

相关网址:

脚本:

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

3

您不应该尝试一次列出所有列表,因为数据库中元素的大小与它存储到内存中的元素大小不同。如果要处理元素,则应为每个循环使用a并利用实体框架的延迟加载,这样就不会立即将所有元素都带入内存。如果要显示列表,请使用分页(.Skip()和.take())


3

随着.Net的发展,他们添加新的32位配置的能力也在不断提高,这似乎使每个人都感到困惑。

如果您使用的是.Net Framework 4.7.2,请执行以下操作:

转到项目属性

建立

取消选中“首选32位”

干杯!


1

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

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

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

所以你对此能做些什么?

如果您使用的是4.5.1之前的.net版本,那么您所能做的就是意识到这一问题并设法避免它。因此,在这种情况下,如果没有列表中包含的车辆不超过18,000个元素,则可以使用车辆列表的清单。这可能会导致一些难看的代码,但这是可行的解决方法。

如果使用的是.net 4.5.1或更高版本,则垃圾收集器的行为已发生了细微变化。如果在要进行大内存分配的位置添加以下行:

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();
  }
}

当然,这仅在您具有可用的物理(或虚拟)内存时才有用。


0

尽管GC会压缩小型对象堆作为优化策略的一部分,以消除内存漏洞,但出于性能原因,GC从未压缩大型对象堆**(大型对象的压缩成本太高(大小超过85KB) )**。因此,如果您正在运行在x86系统中使用许多大对象的程序,则可能会遇到OutOfMemory异常。如果在x64系统上运行该程序,则可能会有碎片堆。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.