使用Insert()和Clear()时StringBuilder中的内存泄漏

时间:2015-04-11 05:31:11

标签: .net memory-leaks stringbuilder

我需要在StringBuilder中添加一些行,其中最后添加的行应该在字符串的开头,最后的行应该在结尾处。我添加了一个这样的新行:

stringBuilder.Insert(0, "Some text." + Environment.NewLine);

完成后,我像这样清空StringBuilder:

stringBuilder.Clear();

我多次重复使用相同的StringBuilder,这会导致内存不足异常。

我用下面的测试程序调查了这个问题,它表明当使用Append()和Clear()时,StringBuilder的容量保持不变,无论使用多少次。但是当使用Insert()和Clear()时,StringBuilder的容量会不断增长。以下是测试结果:

           stringbuilder.Capacity
Iteration  Append Insert
0:         81984,  78016
1:         81984, 155938
2:         81984, 233860
3:         81984, 311782
4:         81984, 389704
5:         81984, 467626
6:         81984, 545548
7:         81984, 623470
8:         81984, 701392

这些测试数据来自下面的测试程序。可以看出,无论一个人执行Append()和Clear()多少次,容量都不会改变。但是在使用Insert()和Clear()时它会继续增长。

问题:使用Insert()和Clear()时StringBuilder是否存在内存泄漏是否正确,因为容量不断增长,最终会导致Out of Memory异常?

要自己测试一下,只需增加outerIndex的上限即可获得异常。

如果这确实被视为内存泄漏,我该如何向Microsoft报告?

using System;
using System.Text;

namespace TestStringBuilder {
  class Program {
    static void Main(string[] args) {
      StringBuilder stringBuilderAppend = new StringBuilder();
      StringBuilder stringBuilderInsert = new StringBuilder();
      string capacityGrow = "";
      for (int outerIndex = 0; outerIndex < 9; outerIndex++) {
        for (int innerIndex = 0; innerIndex < 1000; innerIndex++) {
          stringBuilderAppend.Append("Just some text to fill stringbuffer with 01234567890qwertyuiopasdfghjklzxcvbnm");
          stringBuilderInsert.Insert(0, "Just some text to fill stringbuffer with 01234567890qwertyuiopasdfghjklzxcvbnm");
        }
        stringBuilderAppend.Clear();
        stringBuilderInsert.Clear();
        capacityGrow += outerIndex + ": " + stringBuilderAppend.Capacity + ", " + stringBuilderInsert.Capacity  + Environment.NewLine;
      }
      //check in the debugger capacityGrow to see the capacity used in each iteration
      System.Diagnostics.Debugger.Break();
    }
  }
}

EDIT1: 经过一些搜索后,我发现在Clear()之后将Capacity设置为0的建议,如下所示:

stringBuilder.Clear();
stringBuilder.Capacity = 0;

这可以防止内存泄漏,并在重复使用Insert()时导致Out of Memory异常,但它也无法重用StringBuilder,这是为了防止内存的分配和释放以实现更高的速度。而不是设置Capacity = 0,也可以使用新的StringBuilder而不是Clear()。

似乎Insert()没有重用已经分配给StringBuilder的未使用的内存,但是不管添加Clear(),都会不断添加新的内存。我仍然觉得这是一个错误,因为尽管清除了StringBuilder,谁会期望内存耗尽?如果这是一个错误并且应该向Microsoft报告,我将不胜感激。

EDIT2: 我向微软报告了这个错误: https://connect.microsoft.com/VisualStudio/feedback/details/1242873

请点击链接并提出错误信息,否则我担心微软不会对此进行调查: - (

结束语:

  • 从以前的帖子中我发了一个经验,评论者很快就说我的课程有问题。他们非常粗鲁地评论,即使是他们错了。

  • 在之前的帖子中,我的体验是评论者喜欢将问题标记为&#34;这个问题已经有了答案&#34;,只是因为他们不理解我的问题与其他问题。实际上,对于StringBuilder和Out of Memory,有很多关于stackoverlow的问题。我阅读了google或stackoverlow所能找到的所有内容。它们是关于APPEND()和关于非常长的文本。它们与Insert()无关。请注意,即使使用非常短的字符串并且仅在使用Insert()时出现问题,但在使用Append()时也不会出现问题!

  • 从以前的帖子中我得到的经验是,评论者喜欢建议使用不同的编程,比如避免使用Insert()并通过使用Append并以反向顺序添加行来解决问题。虽然在某些情况下这可能是可能的,但我认为Insert()和Clear()应该正常工作,我们应该让Microsoft知道是否存在问题。

2 个答案:

答案 0 :(得分:3)

我会说这种行为是可以预期的:

Clear()

该文件指出:

  

从当前StringBuilder实例中删除所有字符。[...]   调用Clear方法不会修改当前实例的Capacity或MaxCapacity属性。see Documenation

所以Clear()删除内容但保留内存。

Insert()

此处的文档指出先前的内容已移位,并根据需要调整容量。 (doc)这个并不完全清楚,但我假设分配了新的内存容量+新长度,并将其余内容复制过来。现在。这可能不是最佳解决方案,但我认为这是它的实现方式。因此,可以预期内存不足的情况。


另一个建议:始终插入第一个位置 - 无论实施 - 都非常昂贵。如果只需要一次结果,请考虑使用List<string>,在第0位插入新行,然后迭代列表一次并构建字符串。


修改

在查看(假设)source后,我认为StringBuilder确实为每个插入分配了一个新实例作为'chunk' - 无论其他任何romm可用。 我跟着调用了MakeRoom,在第2044行分配了一个新的块。

答案 1 :(得分:-1)

我们遇到了与

相同的问题
Replace()

当我们将.net 2的网络应用程序更新为.net 4.

显然StringBuilder的实施发生了变化。看到 How the StringBuilder class is implemented? Does it internally create new string objects each time we append?

我们研究的解决方案:

  1. 不要重复使用StringBuilder(这是我们在重写后决定做的事情)
  2. 编写我们自己的SB
  3. 实现
  4. 使用.NET 2.0