更改 StringBuilder 容量时出现异常!

3

这是我的代码行:

StringBuilder myCompleteMessage = new StringBuilder();
myCompleteMessage.Capacity = Int32.MaxValue-1;

我也尝试了这个方法:

myCompleteMessage.Capacity = myCompleteMessage.MaxCapacity-1;

我在第二行遇到了异常。

Exception of type 'System.OutOfMemoryException' was thrown.

堆栈跟踪:

at System.String.GetStringForStringBuilder(String value, Int32 startIndex, Int32 length, Int32 capacity)
at System.Text.StringBuilder.set_Capacity(Int32 value)
at Orca.Server.HandleClientComm(Object newClient) in C:\Users\Dan\Documents\Visual Studio 2010\Projects\msig\Orca\Server.cs:line 100
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart(Object obj)

2
你需要指定容量的原因是什么?通常最好让框架处理容量(除非你确切知道需要多少)。 - Mrchief
如果你需要一个如此大的字符串,你需要重新审视设计,但这是一个好问题。 - Mangesh
“Preforming by design” 是什么意思? - Danpe
它在 x64 中也失败了。 - Mangesh
我认为Danpe的2048是“大约2GB”,而不是2048...但这只是猜测。2048更像是使用StringBuilder的代码中的一个错误。 - Alexei Levenkov
显示剩余2条评论
3个回答

8
假设您使用的是32位系统,那么第二行代码将总是失败。您要求.NET为您的StringBuilder分配4 GB的空间,这比进程可用的内存空间还要多(感谢Joel指出char占用2个字节而不是1个字节)。
编辑: 如果您使用ILSpy查看StringBuilder,则会在Capacity的设置中看到以下代码片段:
if (this.Capacity != value)
{
    int num = value - this.m_ChunkOffset;
    char[] array = new char[num];
    Array.Copy(this.m_ChunkChars, array, this.m_ChunkLength);
    this.m_ChunkChars = array;
}

将容量设置为int.MaxValue - 1,您告诉.NET尝试分配一个4 GB的字符数组,这就是代码失败的原因。


1
哎呀,x64 上也失败了。有没有办法指定更大的进程空间?如果默认的 MaxCapacity 不支持 Int.MaxValue,为什么会设为它呢? - Adam Driscoll
只是好奇 :) 不质疑逻辑 - Adam Driscoll
你在测试中是否编译为x64? - sgtz
2
不要忘记 char 是 16 位 Unicode。1000 的容量是 2000 字节。@Adam,int.MaxValue 是唯一明智的答案,因为 Capacityint。任何更小的 MaxCapacity 都会是不必要的任意限制,因为该类已经受到内存的限制。如果您有一台具有 6-8GB 或更多内存的计算机,我相信您可以将 Capacity 设置为 MaxCapacity,因为它可能会使用 4GB。 - Joel B Fant
1
@Danpe:任何 CPU 只是让您创建一个程序集,该程序集将在 32 位操作系统上运行 32 位,在 64 位操作系统上运行 64 位(由 JIT 编译器选择)。正如 Alexei 在他的回答中指出的那样,CLR 对对象大小施加了自己的限制,因此我先前评论的后半部分是不正确的。它需要连续的内存,这又是另一个复杂的问题。 - Joel B Fant
显示剩余3条评论

2
CLR堆限制为2GB对象(http://blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx - 对于2.0,我认为4.0也是一样的),因此没有任何东西可以分配一个超过该大小的连续内存块。对于字符,它给您大约Int.MaxValue/2个条目。
如果您真的需要管理这么多文本,请查看允许分块的MemoryStream的替代实现或允许分块的数组。如果要保持默认类,请考虑将数据写入临时文件(使用DeleteOnClose创建的临时文件可能甚至不会提交到磁盘,因此与StringBuilder或MemoryStream相比,在每次容量增加时必须复制数据,您会获得更好的性能 - http://msdn.microsoft.com/en-us/library/system.io.fileoptions.aspx)。

0

StringBuilder的Capacity属性是设置为字符串缓冲区预留的字符数。使用UTF-8编码,一个字符最多可以达到4个字节。即使在2字节时,容量为Int32.MaxValue-1对于32位系统来说已经超过了承载能力,仅2字节字符(UTF-8 / ASCII)就占用了4GB的空间。此外,如果查看文档,可以发现StringBuilder的默认Capacity已经设置为Int32.MaxValue。

如果您正在循环填充StringBuilder,则可能填充速度比.NET通过垃圾回收清理分配的内存更快,这就是为什么它可能会在2048个字符长度停止的原因。另外,它需要一个连续的内存块,这也可能会出现问题。

不过,你的问题是关于Capacity属性的,而在32位系统上你永远无法分配那么多的容量。我在MSDN上找到了一个有趣的讨论,涉及到这个具体的问题,并通过测试发现实际容量要低得多,因为当所有字符都是2字节时,Int32.MaxValue容量相当于需要4GB的存储空间。

你能否使用内存映射文件呢?StringBuilder并不适用于此,你需要重新考虑你的设计。


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