我们有一些操作,我们正在进行大量的大型字符串连接,并且最近遇到了内存不足异常。不幸的是,调试代码不是一种选择,因为这是在客户站点发生的。
因此,在研究代码的大修之前,我想问一下:StringBuilder对大字符串的RAM消耗特性是什么?
特别是它们与标准字符串类型相比。字符串的大小超过10 MB,我们似乎遇到了大约20 MB的问题。
注意:这不是速度而是RAM。
答案 0 :(得分:10)
每次StringBuilder空间不足时,它会重新分配两倍于原始缓冲区大小的新缓冲区,复制旧字符,并让旧缓冲区得到GC。您可能只是使用足够的(称之为x),使得2x大于您允许分配的内存。您可能想要确定字符串的最大长度,并将其传递给StringBuilder的构造函数,以便您预先分配,并且您不会受到双倍重新分配的支配。
答案 1 :(得分:6)
这是关于String Concatenation vs Memory Allocation的一项很好的研究。
如果您可以避免连接,请执行此操作!
如果你不这样做,这是没有道理的 必须连接但想要你的 源代码看起来不错,使用 第一种方法。它将被优化为 如果它是一个字符串。
请勿使用+ =连接。正在进行太多更改 在幕后,这并不明显 从我的代码开始。一世 建议改为使用String.Concat() 明确地表示任何重载(2 字符串,3个字符串,字符串数组)。 这将清楚地显示您的代码 没有任何意外,同时 允许自己检查 效率。
尝试估算StringBuilder的目标大小。
你可以更准确地估计出来 需要的大小,不那么临时 StringBuilder必须具有的字符串 创造增加其内部 缓冲液中。
当性能出现问题时,请勿使用任何Format()方法。
涉及太多开销 在可能的情况下解析格式 在构造一个阵列时 所有你使用的是{x}替换。 Format()有利于提高可读性,但是 你是其中的一件事 压缩所有可能的表现 你的申请。
答案 2 :(得分:3)
您可能对绳索数据结构感兴趣。本文:Ropes: Theory and practice解释了它们的优点。也许.NET有一个实现。
[更新,回答评论] 它使用更少的内存吗?在文章中搜索记忆,你会发现一些提示 基本上,是的,尽管有结构开销,因为它只是在需要时添加内存。 StringBuilder在耗尽旧缓冲区时,必须分配一个更大的缓冲区(可能已经浪费了空内存)并丢弃旧的缓冲区(这将是垃圾收集,但同时仍然可以使用大量内存)。
我还没有找到.NET的实现,但至少有一个C ++实现(在SGI的STL中:http://www.sgi.com/tech/stl/Rope.html)。也许你可以利用这个实现。请注意,我引用的页面有关于内存性能的工作。
请注意,绳索并不能解决所有问题:它们的用处在很大程度上取决于您如何构建大型字符串以及如何使用它们。文章指出了优点和缺点。
答案 3 :(得分:1)
Strigbuilder是串联字符串引起的内存问题的完美解决方案。
要回答您的具体问题,Stringbuilder与普通字符串相比具有恒定大小的开销,其中字符串的长度等于当前分配的Stringbuilder缓冲区的长度。缓冲区的大小可能是结果字符串的两倍,但在连接到Stringbuilder之前不会再进行内存分配,直到填充缓冲区为止,因此它确实是一个很好的解决方案。
与字符串相比,这很出色。
string output = "Test";
output += ", printed on " + datePrinted.ToString();
output += ", verified by " + verificationName;
output += ", number lines: " + numberLines.ToString();
此代码有四个字符串,在代码中存储为文字,两个在方法中创建,一个在变量中创建,但它使用六个单独的中间字符串,这些字符串越来越长。如果继续这种模式,它将以指数速率增加内存使用量,直到GC开始清理它。
答案 4 :(得分:-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