2011-12-19 266 views
51

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

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

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

+7

什么是'selectedVehicles'的值(和类型)? – harold 2011-12-19 16:01:15

+1

当抛出'OutOfMemoryException'时,你是否使用调试器附加到进程,并查看可能出现的问题?对象有多大?对于对象大小,.NET Framework的硬限制为2 GB,减去框架本身消耗的开销。 – 2011-12-19 16:05:00

+1

车辆可能是一个结构而不是一个类? – 2011-12-19 16:05:58

回答

57

两点:

  1. 如果您运行的是32位的Windows,你不会有所有的4GB访问,只有2GB。
  2. 不要忘记List的底层实现是一个数组。如果你的内存很碎,可能没有足够的连续空间来分配List,尽管总共有很多可用内存。
+21

在64位Windows中只有2 GB可用。这是.NET Framework的一个限制,而不仅仅是32位地址空间。 – 2011-12-19 16:18:12

+0

+1对于第2点。他可以尝试编写一个分段列表。 – 2011-12-19 16:19:20

+22

@CodyGray这将是每个对象(数组)2GB,而不是总共2GB。 – 2011-12-19 16:21:09

10

存储在数据库中的数据与应用程序中的存储器相比是非常不同的。

没有一个办法让你的对象的确切大小,但你可以这样做:对象一定量已经加载完毕后

GC.GetTotalMemory() 

,看看你的内存是多少改变,因为你加载列表。

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

如果你看看这里:http://www.dotnetperls.com/array-memory你也会看到.NET中的对象比他们的实际数据更大。通用列表甚至比数组更多的是内存。如果你的对象中有一个通用列表,那么它将会增长得更快。

7

OutOfMemoryException异常(在32位机)就是经常约碎片作为存储实际的硬限制 - 你会发现很多关于这一点,但这里是我的第一个谷歌打简要讨论这个问题:http://blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx。 (@Anthony Pegram在他上面的评论中提到了同样的问题)。

这就是说,有可能想到上面代码中另外一个可能性:由于您使用的“IEnumerable的”构造到列表中,你可能给对象的任何提示,以规模您传递给List构造函数的集合。如果传递的对象不是集合(不实现接口),那么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 2011-12-19 16:36:10

3

您不应该尝试一次带入所有列表,数据库中元素的大小与内存中元素的大小不同。 如果你想处理你应该为每个循环使用的元素,并利用实体框架延迟加载,所以你不要一次把所有的元素放入内存中。 如果你想显示列表使用分页(.Skip()和。取())

58

.Net4.5没有一个对象的2GB的限制了。 此行添加到的App.config

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

,这将是可能没有得到OutOfMemoryException异常

创建非常大的对象请注意,它会在x64 OS唯一的工作!

+0

做得很好。这对我有用,注意必须将Build目标更改为x64。 – 2015-10-19 18:42:42

+0

也适用于ASP.NET 4.5?使用本地报告,*我的应用程序v1(asp.net 3.5 - clr 2.0 - 经典)*工程***确定***,但我的应用程序v2(asp.net 4.5,clr 4.0,经典)生成*** OutOfMemoryException错误***,在同一个IIS服务器_ – Kiquenet 2015-10-30 12:48:10

67

3岁的话题,但我发现了另一个工作解决方案。 如果你确定你有足够的可用内存,运行64位操作系统,并仍然得到例外,一定要在你的项目属性来设置这个选项 enter image description here

+1

没有图像存在 – TheGameiswar 2017-01-10 18:19:12

4

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

我们增加将以下Post-Build脚本放入.exe项目并再次编译,将目标设置为x86并增加1.5 gb,同时还使用3.2 gb的x64 Platform目标增加内存。我们的应用程序是32位。

相关网址:

脚本:

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