在 .Net 中,非常大的集合会导致内存不足异常

32

我正在测试在 .Net 中集合能够变得多大。从技术上讲,任何集合对象都可以增长到物理内存的大小。

然后我在一台拥有 16GB 内存、运行 Windows 2003 服务器和 Visual Studio 2008 的服务器上测试了以下代码。我测试了 F# 和 C# 代码,并在运行时查看了任务管理器。我发现,在增长了约 2GB 内存后,程序会崩溃并出现内存不足的异常。我已将目标平台设置为 x64。

open System.Collections.Generic

let d = new Dictionary<int, int>()

for i=1 to 1000000000 do
    d.Add(i,i)

我对C5集合库进行了同样的测试。结果是C5中的字典可能会耗尽整个内存。代码使用了C5:

let d = C5.HashDictionary<int, int> ()
for i=1 to 1000000000 do
    d.Add(i,i)

有人知道为什么吗?


12
物理内存与最大大小没有任何关系,Windows 十多年来一直使用虚拟内存管理。因为在创建新进程时可用的物理内存可能为零,所以可以拥有比可用物理内存远远更大的对象。这就是虚拟内存的全部意义;物理内存仅是虚拟内存的性能优化。如果没有足够的物理内存来容纳工作集,则性能会下降,但一切应该仍然可以正常运行。 - Eric Lippert
2
@EricLippert 我理解并同意你的观点,但是反过来呢?如果有物理内存可用,你是否应该遇到OOM(假设这是真正的OOM而不是太多句柄/类似问题)?在这种情况下,看起来CLR强制限制是问题所在,但这只意味着OP的问题在我看来是一个好问题。 - Basic
4个回答

43

即使是64位版本,Microsoft CLR也有2GB的最大对象大小限制。(我不确定这个限制是否也存在于其他实现中,比如Mono。)

该限制适用于每一个“单一”的对象--而不是所有对象的总大小--这意味着使用某种组合集合相对容易解决。

这里有一个讨论和一些示例代码...

似乎很少有官方文档提到这个限制。毕竟,这只是当前CLR的一个实现细节。我所知道的唯一提到它的地方是此页面上的:

当您在64位Windows操作系统上运行64位托管应用程序时,您可以创建不超过2 GB的对象。


@KMan:哈。在你的评论出现之前,我只用了几秒钟就编辑好了 :) - LukeH
1
你的整篇帖子刚好比我的早几秒钟 :) - Jorge Córdoba
相关链接出现了404错误。 - BurnsBA

22

在 .NET 4.5 以前的版本中,最大对象大小为2GB。从4.5版本开始,启用gcAllowVeryLargeObjects后,你可以分配更大的对象。需要注意的是,string 的限制不受影响,但是“数组”应该也包括“列表”,因为列表是由数组支持的。


11

需要明确的是,Dictionary使用单个数组来添加键值对。每次数组满时都会增长(翻倍?)。当对象达到5.12亿个时,其大小为2GByte(假设采用32位对象指针,且分布完美)。再添加一个元素将使Dictionary尝试再次将数组大小翻倍。Boom。

C5 HashDictionary使用线性哈希,并且可能使用包含多个(16个?)元素的桶数组。它应该会在稍后(更)晚些时候遇到同样的问题。


顺便提一下,C5中的实现是线性链表——对于每个哈希值,它都是一个链接列表。 - Yin Zhu
啊,谢谢。原始的线性哈希论文建议不要这样做。 - Stephan Eggermont

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接