StringBuilder.ToString()抛出OutOfMemoryException

时间:2014-07-29 07:56:06

标签: c# .net-2.0 tostring stringbuilder streamwriter

我创建了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");
}

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

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

3 个答案:

答案 0 :(得分:17)

  

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

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

  

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

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

我很确定代码不是,正如您所描述的那样。这行无法编译:

sw.write(SB.ToString());

...因为该方法是Write而不是write。如果你实际调用SB.ToString(),那么就像str = SB.ToString()一样失败。

您似乎更有可能以流媒体方式写入文件,例如

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

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

答案 1 :(得分:7)

解决方法:假设您希望将存储在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);

答案 2 :(得分:4)

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

此外,执行时

string str = SB.ToString();

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

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