StringBuilder的内存消耗如何?

6
我们有一些操作需要进行大量的字符串拼接,最近遇到了内存溢出异常。不幸的是,由于该问题发生在客户现场,无法进行代码调试。
因此,在着手改进我们的代码之前,我想问一下:当处理大字符串时,StringBuilder 的内存消耗特性如何?特别是与标准的 string 类型相比较而言。这些字符串的大小远超过10 MB,并且似乎在20 MB左右就会出现问题。
请注意:此问题与速度无关,而是与内存有关。
5个回答

10
每当 StringBuilder 空间不足时,它会重新分配一个原始缓冲区两倍大小的新缓冲区,复制旧字符,并让旧缓冲区被垃圾回收。可能是你正在使用足够多(称之为 x),使得 2x 大于允许分配的内存。你可能想为你的字符串确定一个最大长度,并将其传递给 StringBuilder 的构造函数,这样你就可以预分配,而不必受到加倍重新分配的限制。

6

这里有一份关于字符串拼接与内存分配的研究报告。

如果可以避免拼接字符串,就尽量避免!

这是显而易见的,如果你不必拼接字符串但仍想保持代码美观,那么使用第一种方法。它将被优化为一个单独的字符串。

永远不要使用 += 拼接字符串。太多的变化正在后台发生,这些变化并不明显。我建议明确地使用 String.Concat(),使用任何重载(2个字符串、3个字符串、字符串数组)。这将清楚地显示你的代码在做什么,没有任何意外,并允许你保持效率。

尝试估算 StringBuilder 的目标大小。

你越能够准确地估算所需的大小,StringBuilder 就需要创建的临时字符串越少,以增加其内部缓冲区。

当性能成为问题时,请不要使用任何 Format() 方法。

过多的开销涉及到解析格式,而当你只使用 {x} 替换时,你可以将数组构造出来。Format() 对可读性有好处,但当你将所有可能的性能挤出你的应用程序时,它是要被放弃的事情之一。


3
您可能会对绳数据结构感兴趣。这篇文章:Ropes: Theory and practice解释了它们的优点。也许有.NET的实现。
[更新,回答评论] 它是否使用更少的内存?在文章中搜索memory,您会找到一些提示。 基本上是的,尽管有结构开销,因为它只在需要时添加内存。StringBuilder在耗尽旧缓冲区时必须分配一个更大的缓冲区(可以浪费空闲内存),并且放弃旧缓冲区(虽然可以在此期间使用大量内存)。
我没有找到.NET的实现,但至少有一个C++实现(在SGI的STL中:http://www.sgi.com/tech/stl/Rope.html)。也许您可以利用这个实现。请注意,我引用的页面上有关于内存性能的工作。
请注意,绳并不是解决所有问题的灵丹妙药:它们的实用性严重依赖于如何构建大型字符串以及如何使用它们。这篇文章指出了优点和缺点。

有没有关于它的RAM表现而不是速度表现的数据? - torial
我会通过更新我的回复来回答您的问题。希望您能收到对评论的回答通知。 - PhiLho

1

Strigbuilder是解决由于字符串拼接导致的内存问题的完美解决方案。

回答你具体的问题,与普通字符串相比,Stringbuilder有一个固定大小的开销,其中字符串的长度等于当前分配的Stringbuilder缓冲区的长度。缓冲区可能最多是结果字符串的两倍大小,但在将字符串连接到Stringbuilder时,不会进行更多的内存分配,直到填满缓冲区,所以这确实是一个出色的解决方案。

与字符串相比,这是杰出的。

string output = "Test";
output += ", printed on " + datePrinted.ToString();
output += ", verified by " + verificationName;
output += ", number lines: " + numberLines.ToString();

这段代码有四个字符串,它们作为字面量存储在代码中,另外两个字符串是在方法中创建的,还有一个来自变量。但是它使用了六个单独的中间字符串,这些字符串会越来越长。如果这种模式继续下去,它将以指数级别增加内存使用率,直到GC启动清理它。


-2

我不确定字符串构建器的内存模式,但普通字符串不是一个好选择。

当你使用普通字符串时,每次连接都会创建另外一对字符串对象,内存消耗急剧增加,导致垃圾回收器被频繁调用。

string a = "a";

//creates object with a

a += "b"

/creates object with b, creates object with ab, assings object with ab to "a" pointer

在依赖此功能之前,我建议您先检查字符串类的实现。至少在Java中,现在比那更智能了。我没有检查过.net的实现,但我看不出为什么他们不会进行相同的优化。 - Bill K

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