今天今天我的应用程序抛出了OutOfMemoryException
。对我来说这一直是几乎是不可能的,因为我有4GB RAM和大量的虚拟内存了。当我试图现有集合添加到一个新的列表发生错误。
List<Vehicle> vList = new List<Vehicle>(selectedVehicles);
据我了解这里存在分配,因为我的车新名单应该已经包含内存中不存在的内存。我不得不承认Vehicle
是一个非常复杂的类,我试图约50.000项目一次添加到新的列表。但由于应用程序中所有Vehicle
s来自那些只有200MB大小数据库:我不知道可以在这一点引起OutOfMemoryException
。
两点:
List
的是一个数组。如果你的内存是大量碎片,可能没有足够的连续空间分配给您的List
,即使在总你有足够的可用内存。随着净进步,所以他们不会增加新的32位配置起来,似乎每个人都跳闸能力。
如果你是在.Net框架4.7.2做到以下几点:
转到项目属性
建立
取消选中“喜欢的32位”
干杯!
3岁的话题,但我发现了另一个工作液。如果你确定你有足够的可用内存,运行64位操作系统,并仍然得到例外,去Project properties
- > Build
选项卡,并确保设置x64
为Platform target
。
.Net4.5没有为对象2GB的限制了。收藏此行的app.config
<runtime>
<gcAllowVeryLargeObjects enabled="true" />
</runtime>
这将是可能没有得到OutOfMemoryException异常创建非常大的对象
请注意,它会在x64 OS唯一的工作!
存储在数据库中的数据相比,存储在您的应用程序是非常不同的。
有没有办法让你的对象的确切大小,但你可以这样做:
GC.GetTotalMemory()
之后的对象一定量已加载,看看有多少,你加载列表中你的记忆正在发生变化。
如果是引起过多的内存使用情况,然后我们可以看看如何尽量减少它的列表。比如为什么你要在第一时间加载到内存中一次全部50000个对象。那岂不是最好致电DB根据您的需要呢?
如果你看看这里:http://www.dotnetperls.com/array-memory你也会看到,在.NET对象是比实际数据更大。一个通用的列表是更内存猪比阵列。如果你有你的对象内通用的清单,然后它会增长得更快。
OutOfMemoryException异常(在32位机)就是经常约碎片作为存储实际的硬限制 - 你会发现很多关于这一点,但这里是我的第一个谷歌打简要讨论这个问题:http://blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx。 (@Anthony Pegram指的是同样的问题,在他的评论以上)。
这就是说,有可能想到上面代码中另外一个可能性:由于您使用的“IEnumerable的”构造到列表中,你可以不给该对象的任何提示为您传递集合的大小该列表构造。如果你传球的对象不是一个集合(未实现ICollection
接口),那么幕后的列表实施将需要增加几个(或多个)次,每次留下了过小的到被垃圾收集阵列需要。垃圾收集器可能不会得到那些丢弃阵列速度不够快,你会得到你的错误。
造成这种情况的最简单的解决将是使用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的。
我的开发团队解决了这一情况:
添加了下面的生成后脚本到.exe项目并重新编译,目标设置为x86和1.5 GB的增加,使用3.2 GB也是x64平台的目标增加内存。我们的应用是32位。
相关网址:
脚本:
if exist "$(DevEnvDir)..\tools\vsvars32.bat" (
call "$(DevEnvDir)..\tools\vsvars32.bat"
editbin /largeaddressaware "$(TargetPath)"
)
你不应该试图把所有的名单一次,在数据库中的元素的TE尺寸是不一样的是,一个需要到内存中。如果你要处理的元素,你应该使用每个循环,并采取实体框架延迟加载的优势,所以你不要把所有的元素到内存中一次。如果你想显示列表使用分页(.Skip()和。取())
虽然GC压实小对象堆作为优化战略的一部分,以消除内存洞,GC从未压实性能方面的原因大对象堆**(压缩的成本太高了大对象(大于85KB的大小) )**。因此,如果你正在运行在x86系统上使用了很多大型对象的程序,你可能会遇到内存不足的异常。如果您在x64系统上运行的程序,你可能有一个支离破碎的堆。
我知道这是一个老问题,但由于没有一个答案中提到的大对象堆,这可能是使用别人谁发现这个问题...
在.NET任何内存分配即超过85,000个字节来自大对象堆(LOH)不正常的小对象堆。为什么会发生这种事?由于大对象堆不板结。这意味着大对象堆得支离破碎,在我的经验,这不可避免地会导致内存不足的错误。
在原来的问题列表中有五万件。内部列表使用阵列,并假设32比特,需要50000 X 4字节= 200,000字节(或两倍,如果64位)。这样的内存分配从大对象堆的到来。
所以你对此能做些什么?
如果您使用的是.NET之前的版本4.5.1,然后所有你可以做的是要注意的问题,并尽量避免它。所以,在这种情况下,而不是有车辆列表,你可以拥有车辆的名单列表,没有提供列表过比它约18000元以上。这可能会导致一些丑陋的代码,但它是可行的周围工作。
如果您使用的是.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();
}
}
当然,如果你有物理(或虚拟)内存可用此才有帮助。