StringBuilder.ToString()抛出OutOfMemoryException

Thi*_*iru 11 c# stringbuilder tostring streamwriter .net-2.0

我创建了一个StringBuilder长度"132370292",当我尝试使用ToString()它抛出的方法获取字符串OutOfMemoryException.

StringBuilder SB = new StringBuilder();

for(int i =0; i<=5000; i++)
{
    SB.Append("Some Junk Data for testing. My Actual Data is created from different sources by Appending to the String Builder.");
}

try
{
    string str = SB.ToString(); // Throws OOM mostly
    Console.WriteLine("String Created Successfully");
}
catch(OutOfMemoryException ex)
{
    StreamWriter sw = new StreamWriter(@"c:\memo.txt", true);
    sw.Write(SB.ToString()); //Always writes to the file without any error
    Console.WriteLine("Written to File Successfully");
}
Run Code Online (Sandbox Code Playgroud)

创建新字符串时OOM的原因是什么?为什么在写入文件时它不会抛出OOM?

机器详细信息:64位,Windows-7,2GB RAM,.NET 2.0版

Jon*_*eet 19

创建新字符串时OOM的原因是什么

因为内存不足 - 或者至少CLR无法分配具有您请求的大小的对象.这真的很简单.如果要避免错误,请不要尝试创建不适合内存的字符串.请注意,即使您有大量内存,即使您运行的是64位CLR,也可以创建可以创建的对象大小.

为什么它在写入文件时不会抛出OOM?

因为你有比内存更多的磁盘空间.

我很确定代码并不完全像你描述的那样.这行无法编译:

sw.write(SB.ToString());
Run Code Online (Sandbox Code Playgroud)

...因为方法Write不是write.如果你真的打电话SB.ToString(),那就像失败一样str = SB.ToString().

它似乎更容易,你实际上是写入文件以流方式,如

using (var writer = File.CreateText(...))
{
    for (int i = 0; i < 5000; i++)
    {
        writer.Write(mytext);
    }
}
Run Code Online (Sandbox Code Playgroud)

这样你就不需要在内存中有大量的文本 - 它只是将它写入磁盘,可能有一些缓冲,但不足以引起内存问题.

  • 如果他在两种情况下都调用`ToString`,那么他写入磁盘的事实不是一个不相关的细节吗? (4认同)
  • 两个可能非64位进程都会有足够的内存,但可能会运行32位并且会遇到地址空间碎片; 它可能成功写入磁盘,因为它已经将结果转换为字符串一次并且之前的调用没有失败(基于示例代码) (4认同)
  • @ LasseV.Karlsen:看到我的更新 - 这不是真正的代码. (2认同)

Tit*_*tus 8

解决方法:假设您希望将存储在StringBuilder中的大字符串写入StreamWriter,我会以这种方式编写以避免SB.ToString的OOM异常.但是如果您的OOM异常是由于StringBuilder的内容添加自己,那么您应该对此进行处理.

public const int CHUNK_STRING_LENGTH = 30000;
while (SB.Length > CHUNK_STRING_LENGTH )
{
    sw.Write(SB.ToString(0, CHUNK_STRING_LENGTH ));
    SB.Remove(0, CHUNK_STRING_LENGTH );
}
sw.Write(SB);
Run Code Online (Sandbox Code Playgroud)

  • 您可能必须考虑编码的方式,而不是您希望代码的方式.我相信我的上述代码运行良好,并鼓励您调试和验证它. (3认同)
  • 这对我获得最终解决方案很有帮助。我只想指出 3 个错误。首先,它应该是 (SB.Length &gt; 0) 作为条件,否则你将丢失最后几千条数据。其次,您可能在最后一次运行中,这意味着您不能使用完整的块长度,而应该检查这种情况并使用剩余的长度(以避免 OutOfArgumentRangeException)。第三,我认为最后一行是多余的,是一个意外。 (2认同)
  • 是的你是对的,这是正确的.以你的方式做它看起来更好.对不起,我怀疑你:).虽然我会更好地命名变量(stringBuilder,为ToString创建一个"缓冲区"变量). (2认同)

sem*_*mao 6

您必须记住,.NET 中的字符串以 16 位 unicode 存储在内存中。这意味着长度为 132370292 的字符串将需要 260MB 的 RAM。

此外,在执行时

string str = SB.ToString();
Run Code Online (Sandbox Code Playgroud)

您正在创建字符串的 COPY(另一个 260MB)。

请记住,每个进程都有自己的 RAM 限制,因此即使您还有一些空闲 RAM,也可能抛出 OutOfMemoryException。