StringBuilder的最大容量

6
为什么当i=690864192时,这段代码会抛出OutOfMemoryException异常?
StringBuilder sb = new StringBuilder();
                for (int i = 0; i < Int32.MaxValue; i++)
                {
                    sb.Append("s");
                }
                Console.WriteLine(sb.ToString());
                Console.Read();

默认的容量为16个字符,但它会随着需要增加到最大值,即int.MaxValue = 2,147,483,647。那么当字符数为690,864,192时,远小于最大容量,为什么会抛出异常?


1
所以基本上我没有足够的RAM来存储那么多字符的StringBuilder实例? - David Klempfner
那时你已经分配了1.29GB的内存。这是一个巨大的块! - Harrison
如果我有一个类,它持有一个字节数组,并且它有一个名为“Int64 MaxCapacity { get; }” 的公共属性,它总是返回99999999999999999,这是否意味着计算机具有那么多内存? - ta.speot.is
@Harrison 我相信1.3GB是.NET的GC和32位地址空间的一个魔法数字。从那时起,它的性能表现并不特别好。 - ta.speot.is
@ta.speot.is 这篇代码项目文章解释了运行时需要600-800MB的内存。默认设置是应用程序只能使用2GB。实际内存限制约为1.3 GB。 - Harrison
2个回答

35
所以基本上,我没有足够的RAM来存储那么多字符的StringBuilder实例吗?
不不不不不。
RAM是不相关的; RAM在近20年来一直不是相关的内存度量单位!虚拟地址空间是相关的度量单位。
许多人认为内存仍然像1980年代的DOS机器一样工作,这是令人惊讶的。我的建议是学习现代操作系统如何处理内存。 RAM是性能优化。内存是页面文件。
这样考虑:你有一个停车场(页面文件),可以容纳一百万辆汽车(内存页面)。你有一条车道(RAM)可以容纳十辆车。你有一个钥匙扣可以容纳一千把钥匙(虚拟内存)。你拥有一千辆汽车。你经常使用的10辆在车道上。其他990辆在街对面的停车场里。当你在车道上没地方时,只需将其中一辆车移到停车场即可。(RAM使访问频繁使用的页面快速)。
但是当你购买第1001辆车时,你已经用完的资源是钥匙扣上的空间,而不是停车场或车道上的空间。您随时可以在车道上腾出更多的空间,在停车场里有很多额外的空间,但您的钥匙扣只能容纳那么多。
为什么当字符数为690,864,192时, 少于最大容量时,会抛出异常?
在32位Windows机器上,每个进程只有2GB的可寻址虚拟地址空间。

如果每个字符占两个字节,那么6.9亿个字符就是14亿字节,占用了2GB地址空间的一个巨大部分。你只剩下约0.6GB来适配进程中的其他所有内容。在某一时刻,字符串生成器需要分配另一个块,但你的地址空间中没有任何大小相同的空闲块,因此分配失败。

首先,为什么你要这样做呢?一个14亿字节的字符串完全荒唐。


4
我只是感到无聊,想要尝试一些实验。我之前曾经做过类似继承的事情。 - David Klempfner
2
@DavidKlempfner 那是一个很棒的理由 :) - AjLearning

6
每次StringBuilder(字符串构建器)通过指向前一个chunk实例(另一个StringBuilder)来分配新的char[](字符数组),你就会给垃圾回收器带来巨大的压力。
对象大小存在内存限制。在达到最大值之前,你的对象很可能会占用应用程序进程所分配的所有内存,因此在任一情况下都会发生OutOfMemoryException(内存不足异常)。
你最后拥有的StringBuilder实例指向它达到最大值之前的最后一个实例...那个实例也指向它达到最大值之前的前一个实例...等等。你拥有一个巨大的GC根图,永远不会被清理。

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