我需要在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知道是否存在问题。
答案 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?
我们研究的解决方案: