C # : 内存不足异常

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

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

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

243229 次浏览

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

没有办法得到你的物体的确切大小,但你可以这样做:

GC.GetTotalMemory()

在加载了一定数量的对象之后,查看加载列表时内存的变化情况。

如果是这个列表导致了过度的内存使用,那么我们可以考虑如何将其最小化。比如,为什么一开始要将50,000个对象同时加载到内存中。按你的要求给死者打电话不是更好吗?

如果您在这里看一下: http://www.dotnetperls.com/array-memory,您还将看到。NET 比他们的实际数据大。泛型列表比数组更占用内存。如果在对象内部有一个通用列表,那么它会增长得更快。

两点:

  1. 如果您运行的是32位的 Windows,那么您将不能访问所有的4GB,只能访问2GB。
  2. 不要忘记,List的底层实现是一个数组。如果您的内存严重碎片化,可能没有足够的连续空间来分配 List,即使您总共有足够的空闲内存。

OutOfMemory 异常(在32位机器上)与碎片一样经常出现在内存的实际硬限制上——你会发现很多这方面的内容,但这里是我第一次在谷歌上简要讨论这个问题: http://blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx。(@Anthony Pegram 在他上面的评论中提到了同样的问题)。

也就是说,对于上面的代码,还有另外一种可能性: 当您使用 List 的“ IEnumable”构造函数时,不会给对象任何关于传递给 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 的情况下才会出现这个问题。

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

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

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

不需要 OutOfMemory 异常就可以创建非常大的对象

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

3年前的话题,但我找到了另一个可行的解决方案。 如果您确定有足够的空闲内存,运行64位操作系统并且仍然有异常,请转到 Project properties-> Build选项卡,并确保将 x64设置为 Platform target

enter image description here

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

我们将以下 Post-Build 脚本添加到。将目标设置为 x86,增加1.5 gb,也将 x64平台目标设置为使用3.2 gb 增加内存。我们的应用程序是32位的。

相关网址:

剧本:

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

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

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

在最初的问题中,列表中有50,000个项目。在内部,列表使用一个数组,假设32位需要50,000 x 4字节 = 200,000字节(如果是64位,则为20,000字节)。所以内存分配来自大型对象堆。

那你能做什么?

如果使用。在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();
}
}

当然,这只有在物理(或虚拟)内存可用的情况下才有帮助。

虽然 GC 压缩小对象堆是消除内存空洞的优化策略的一部分,但出于性能原因,GC 从不压缩大对象堆 * * (压缩的成本对于大对象来说太高(大于85KB)) * * 。因此,如果运行的程序在 x86系统中使用许多大对象,则可能会遇到 OutOfMemory 异常。如果在 x64系统中运行该程序,则可能会产生一个碎片堆。

随着.Net 的发展,他们添加新的32位配置的能力似乎也越来越强。

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

转到项目属性

建造

取消选中“喜欢32位”

干杯!

我挣扎了一下如何得到64位在骑士工作。Rider 上的开发人员需要手动添加解决方案配置。这是为我准备的。

enter image description here