首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Insert()和Clear()时StringBuilder中的内存泄漏

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

Stack Overflow用户
提问于 2015-04-11 05:31:10
回答 2查看 2K关注 0票数 4

我需要向StringBuilder添加一些行,其中最后添加的行应该在字符串的开头,而最老的行应该在末尾。我增加了一个新的行如下:

代码语言:javascript
复制
stringBuilder.Insert(0, "Some text." + Environment.NewLine);

一旦完成,我就像这样清空StringBuilder:

代码语言:javascript
复制
stringBuilder.Clear();

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

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

代码语言:javascript
复制
           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

这些测试数据来自下面的测试程序。正如我们所看到的那样,无论你做了多少次追加()和清除(),容量都不会改变。但在使用Insert()和Clear()时,它确实会不断增长。

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

要自己测试它,只需增加outerIndex的上限以获得异常。

如果这确实应该被认为是内存泄漏,我如何向微软报告呢?

代码语言:javascript
复制
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:在进一步搜索之后,我发现在清除()之后将容量设置为0的建议,如下所示:

代码语言:javascript
复制
stringBuilder.Clear();
stringBuilder.Capacity = 0;

这防止了重复使用Insert()时内存泄漏和内存异常的产生,但它也违背了重用StringBuilder的目的,即防止内存的分配和取消分配以获得更高的速度。相反,要设置Capacity=0,最好使用一个新的StringBuilder,而不是Clear()。

看来Insert()并没有重用已经分配给StringBuilder的未使用内存,而是继续添加新内存,尽管使用了Clear()。我仍然觉得这是一个bug,因为谁会期望在清除StringBuilder的情况下耗尽内存?如果这是一个bug,如果它应该报告给微软,我会很感激你的评论。

Edit2: --我向微软报告了这个bug:https://connect.microsoft.com/VisualStudio/feedback/details/1242873

请按照这个链接并更新错误,否则我担心微软不会调查它:

闭幕词:

  • 从以前的帖子中,我得到了一种经验,评论者很快就会说我的程序有问题。他们当时的评论相当粗鲁,即使是他们错了。
  • 从以前的帖子中,我发现评论者喜欢把一个问题标记为“这个问题已经有答案了”,只是因为他们不理解我的问题和其他问题之间的区别。实际上,对于StringBuilder和内存不足,还有许多关于堆栈过低的问题。我阅读了所有我能找到的谷歌或stackoverlow。它们是关于附加()和非常长的文本。它们不是关于Insert()的。请注意,即使使用非常短的字符串,也只在使用Insert()时会出现问题,而使用Append()时就不会出现问题!
  • 从之前的文章中,我得到了评论者建议使用不同编程的经验,比如避免使用Insert(),并通过使用追加和以倒排顺序添加行来解决问题。虽然在某些情况下这可能是可能的,但我认为Insert()和Clear()应该正常工作,如果有问题,我们应该让Microsoft知道。
EN

回答 2

Stack Overflow用户

发布于 2015-04-11 05:50:44

我想说的是,这种行为是预料之中的:

代码语言:javascript
复制
Clear()

医生说:

从当前StringBuilder实例中移除所有字符.调用Clear方法不会修改当前实例的容量或MaxCapacity属性.见文献资料

因此,Clear()删除了内容,但保留了分配的内存。

代码语言:javascript
复制
Insert()

在这里,doc声明以前的内容被转移,容量根据需要进行调整。(文档这一点还不完全清楚,但我假设分配了新的内存容量+新长度,并复制了其他内存容量。现在。这可能不是最佳解决方案,但我认为这就是实现它的方式。所以记忆不足的情况是意料之中的。

另一个建议是:无论实现如何,总是在第一个位置插入是相当昂贵的。如果只需要结果一次,可以考虑使用List<string>,在0位置插入新行,然后迭代列表一次,然后构建字符串。

编辑:

在查看了(假设的) 来源之后,我认为StringBuilder确实为每一个插入分配了一个新的实例,作为‘块’--不管其他任何可用的romm。我跟踪调用到MakeRoom,在第2044行分配一个新块。

票数 3
EN

Stack Overflow用户

发布于 2015-08-11 23:42:40

我们遇到了同样的问题

代码语言:javascript
复制
Replace()

当我们将我们的网络应用程序从.net 2升级到.net 4时。

显然,StringBuilder的实现发生了变化。请参阅StringBuilder类是如何实现的?每次追加时,它是否在内部创建新的字符串对象?

我们所研究的解决办法:

  1. 不要重用StringBuilder (这是我们在一次大重写之后决定做的)
  2. 写出我们自己对某人的实现
  3. 使用.NET 2.0
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29574469

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档