StringBuilder的RAM消耗是多少?

时间:2020-03-06 14:53:02  来源:igfitidea点击:

我们有一些操作正在处理大量的大字符串连接,并且最近遇到了内存不足的异常。不幸的是,调试代码不是一种选择,因为这是在客户现场进行的。

因此,在仔细检查我们的代码之前,我想问一下:大字符串的StringBuilder的RAM消耗特征是什么?

特别是当它们与标准字符串类型进行比较时。字符串的大小远远超过10 MB,我们似乎遇到了大约20 MB的问题。

注意:这与速度无关,而是RAM。

解决方案

每次StringBuilder用完空间时,它将重新分配一个新缓冲区,其大小是原始缓冲区大小的两倍,复制旧字符,并让旧缓冲区获得GC。我们可能只使用了足够的内存(称为x),使得2x大于允许分配的内存。我们可能想要确定字符串的最大长度,并将其传递给StringBuilder的构造函数,以便进行预分配,而不必再加倍分配。

我不知道字符串生成器的确切存储模式,但是普通字符串不是一个选项。

当我们使用公用字符串时,每个串联都会创建另外两个字符串对象,并且内存消耗激增,从而使垃圾回收器被频繁调用。

string a = "a";

//creates object with a

a += "b"

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

Strigbuilder是解决由串联字符串引起的内存问题的完美解决方案。

为了回答特定问题,与普通字符串相比,Stringbuilder具有固定大小的开销,在普通字符串中,字符串的长度等于当前分配的Stringbuilder缓冲区的长度。缓冲区的大小可能是生成的字符串大小的两倍,但是当连接到Stringbuilder直到缓冲区被填满时,将不会进行更多的内存分配,因此,这确实是一个很好的解决方案。

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

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

该代码有四个字符串,它们作为文字存储在代码中,其中两个是在方法中创建的,一个是通过变量创建的,但是它使用六个单独的中间字符串,字符串的长度越来越长。如果继续执行此模式,它将以指数速率增加内存使用量,直到GC启动清理为止。

这是有关字符串连接与内存分配的不错的研究。

If you can avoid concatenating, do it!
  
  This is a no brainer, if you don't
  have to concatenate but want your
  source code to look nice, use the
  first method. It will get optimized as
  if it was a single string.
  
  Don't use += concatenating ever. Too much changes are taking place
  behind the scene, which aren't obvious
  from my code in  the first place. I
  advise to rather use String.Concat()
  explicitly with any overload (2
  strings, 3 strings, string array).
  This will clearly show what your code
  does without any surprises, while
  allowing yourself to keep a check on
  the efficiency.
  
  Try to estimate the target size of a StringBuilder.
  
  The more accurate you can estimate the
  needed size, the less temporary
  strings the StringBuilder will have to
  create to increase its internal
  buffer.
  
  Do not use any Format() methods when performance is an issue.
  
  Too much overhead is involved in
  parsing the format, when you could
  construct an array out of pieces when
  all you are using are {x} replaces.
  Format() is good for readability, but
  one of the things to go when you are
  squeezing all possible performance out
  of your application.

我们可能对绳索数据结构感兴趣。本文:绳索:理论和实践解释了它们的优点。也许有.NET的实现。

[更新,回答评论]
它使用更少的内存吗?在文章中搜索内存,我们会发现一些提示。
基本上,是的,尽管存在结构开销,因为它仅在需要时添加内存。 StringBuilder在耗尽旧缓冲区时,必须分配一个更大的缓冲区(这可能已经浪费了空的内存),并丢弃了旧缓冲区(将被垃圾回收,但在此期间仍可以使用很多内存)。

我还没有找到.NET的实现,但是至少有一个C ++实现(在SGI的STL中:http://www.sgi.com/tech/stl/Rope.html)。也许我们可以利用此实现。请注意,我参考的页面在内存性能方面有研究。

请注意,绳索并不是解决所有问题的良方:绳索的用途在很大程度上取决于我们如何构造大弦以及如何使用它们。文章指出了优点和缺点。