StringBuilder.ToString()抛出OutOfMemoryException

我有一个长度为“132370292”的StringBuilder ,当我尝试使用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版

创建新字符串时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); } } 

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

解决方法:假设您希望将存储在StringBuilder中的大字符串写入StreamWriter,我会以这种方式编写以避免SB.ToString的OOMexception。 但是如果你的OOMexception是由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); 

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

此外,在执行时

 string str = SB.ToString(); 

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

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