C#:内存异常

问题描述 投票:63回答:10

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

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

据我了解这里存在分配,因为我的车新名单应该已经包含内存中不存在的内存。我不得不承认Vehicle是一个非常复杂的类,我试图约50.000项目一次添加到新的列表。但由于应用程序中所有Vehicles来自那些只有200MB大小数据库:我不知道可以在这一点引起OutOfMemoryException

c# out-of-memory
10个回答
72
投票

两点:

  1. 如果您运行的是32位的Windows,你不会有所有的4GB访问,只有2GB。
  2. 不要忘了,在底层实现List的是一个数组。如果你的内存是大量碎片,可能没有足够的连续空间分配给您的List,即使在总你有足够的可用内存。

1
投票

随着净进步,所以他们不会增加新的32位配置起来,似乎每个人都跳闸能力。

如果你是在.Net框架4.7.2做到以下几点:

转到项目属性

建立

取消选中“喜欢的32位”

干杯!


80
投票

3岁的话题,但我发现了另一个工作液。如果你确定你有足够的可用内存,运行64位操作系统,并仍然得到例外,去Project properties - > Build选项卡,并确保设置x64Platform target


67
投票

.Net4.5没有为对象2GB的限制了。收藏此行的app.config

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

这将是可能没有得到OutOfMemoryException异常创建非常大的对象

请注意,它会在x64 OS唯一的工作!


13
投票

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

有没有办法让你的对象的确切大小,但你可以这样做:

GC.GetTotalMemory() 

之后的对象一定量已加载,看看有多少,你加载列表中你的记忆正在发生变化。

如果是引起过多的内存使用情况,然后我们可以看看如何尽量减少它的列表。比如为什么你要在第一时间加载到内存中一次全部50000个对象。那岂不是最好致电DB根据您的需要呢?

如果你看看这里:http://www.dotnetperls.com/array-memory你也会看到,在.NET对象是比实际数据更大。一个通用的列表是更内存猪比阵列。如果你有你的对象内通用的清单,然后它会增长得更快。


8
投票

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的。


5
投票

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

添加了下面的生成后脚本到.exe项目并重新编译,目标设置为x86和1.5 GB的增加,使用3.2 GB也是x64平台的目标增加内存。我们的应用是32位。

相关网址:

脚本:

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

3
投票

你不应该试图把所有的名单一次,在数据库中的元素的TE尺寸是不一样的是,一个需要到内存中。如果你要处理的元素,你应该使用每个循环,并采取实体框架延迟加载的优势,所以你不要把所有的元素到内存中一次。如果你想显示列表使用分页(.Skip()和。取())


3
投票

虽然GC压实小对象堆作为优化战略的一部分,以消除内存洞,GC从未压实性能方面的原因大对象堆**(压缩的成本太高了大对象(大于85KB的大小) )**。因此,如果你正在运行在x86系统上使用了很多大型对象的程序,你可能会遇到内存不足的异常。如果您在x64系统上运行的程序,你可能有一个支离破碎的堆。


1
投票

我知道这是一个老问题,但由于没有一个答案中提到的大对象堆,这可​​能是使用别人谁发现这个问题...

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

当然,如果你有物理(或虚拟)内存可用此才有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.