0

给定两台电脑

  1. Core i5 第三代 4GB RAM(戴尔 Optiflex 7010)
  2. 酷睿 i7 第二代 16GB RAM(三星 700G7C)

更新 1 - 有趣的是,将 T 的属性数量减少两倍没有任何变化,发生 OOM 的集合中 T 的数量保持不变......

拿平均低频,我只是没有他们手。

谁能向我解释一下,为什么第一台 PC 上运行良好的代码在第二台 PC 上 100% 失败并出现 OutOfMemoryException?考虑到第二台 PC 的成本要高出 3 倍,这真的很烦人。

我不能在这里发布整个代码,但它非常简单 - 一个 List 填充有 T,其中 T 是一个具有 5 个 Integer 属性的 CLR 对象。第一台 PC 处理 2500 万个对象没问题(这就是我所看到的,它可能可以处理更多),而第二台 PC 大约死了。1650 万条记录。我知道单个对象的内存限制,但它真正让我明白的是它如何在两台相当现代的 PC 之间偏离这么多(50%++)?

4

3 回答 3

3

这个问题的文档记录严重不足,但我可以对其中的一些进行逆向工程。首先,内存不足的唯一方法是使用结构列表而不是类。这使得结构大小为 4 * 5 = 20 字节。对于 1650 万个元素,您需要一个用于 List<> 的内部数组,该数组至少具有 20 x 16.5 = 330 兆字节的连续虚拟内存。下一次分配将要求双倍大小,即 660 兆字节。这在 32 位进程中很难实现。OOM 的风险与该大小非常接近。

问题是您需要连续分配。换句话说,虚拟内存地址空间中至少有 660 兆字节的未使用空洞。问题是VM空间需要由代码和数据共享。代码是这里常见的麻烦制造者。一个在一台机器上而不是另一台机器上加载的 DLL。就像来自英特尔、供应商或病毒扫描程序的铲子一样。DLL 有一个可能非常尴尬的首选加载地址,将可用地址空间分成两个较小的部分。这些部分的总和仍然足够大,但没有留下足够大的孔来容纳 660 MB 的分配。

这是一个被称为“地址空间碎片”的普遍问题。它始终是 OOM 的第一个原因,很难消耗所有 2 GB 的可用地址空间。这只能通过进行非常小的分配来实现。

你可以做几件非常简单的事情来解决这个问题:

  • 摆脱那台机器上的铲子,使用 SysInternals 的 VMMap 实用程序查看是否有效
  • 使用类而不是结构。现在 List 元素大小只有 4 个字节而不是 20 个
  • 使用 List<>.Capacity 属性。这可以减少您在列表增长时多次重新分配内部数组而产生的地址空间碎片。只要你有一个好的初步猜测。
  • 你有很好的硬件,使用它。将 EXE 项目上的 Target platform 设置更改为 AnyCPU。64 位进程有大量的地址空间,很难全部使用。
  • 您可以使用 EXE 上的 /LARGEADDRESSAWARE 选项运行 Editbin.exe。现在,您的 32 位进程在 64 位操作系统上将拥有 4 GB 的地址空间。
于 2013-04-15T20:58:20.593 回答
1

这听起来可能很奇怪,但很容易排除。使用内存分析器并注意泄漏源。

我能找出低规格硬件存活而高规格硬件失败的唯一方法是内存碎片。GC不擅长这个。

于 2013-04-21T01:46:28.900 回答
0

将构建平台设置为x64(默认设置为任何平台)解决了我的问题。我尝试了一些很好的方法,包括 gcAllowVeryLargeObjects并强制 x86,但无济于事。

于 2013-04-15T20:51:52.683 回答