web-dev-qa-db-ja.com

C#Stringbuilder OutOfMemoryException

以下の関数を書きました

public void TestSB()
{
  string str = "The quick brown fox jumps over the lazy dog.";
  StringBuilder sb = new StringBuilder();
  int j = 0;
  int len = 0;

  try
  {
     for (int i = 0; i < (10000000 * 2); i++)
     {
        j = i;
        len = sb.Length;
        sb.Append(str);
     }

    Console.WriteLine("Success ::" + sb.Length.ToString());
  }
  catch (Exception ex)
  {
      Console.WriteLine(
          ex.Message + " :: " + j.ToString() + " :: " + len.ToString());
  }
}

今、私は、stringbuilderには20億文字(正確には2,147,483,647)を超える能力があると思います。

しかし、上記の関数を実行すると、約8億の容量に達すると、「System.OutOfMemoryException」が発生しました。さらに、同じメモリと同じような負荷量を持つ異なるPCで、大きく異なる結果が見られます。

誰かが私にこれの理由を提供または説明してもらえますか?.

18
Atur

各文字には2バイトが必要です(.NETのcharはUTF-16コード単位であるため)。したがって、8億文字に達するまでに、1.6GBの連続メモリが必要になります1。 StringBuilderがそれ自体のサイズを変更する必要がある場合は、新しいサイズのanother配列(容量を2倍にしようとしていると思います)を作成する必要があります。これは、3.2GBの配列を割り当てようとすることを意味します。

I believe CLR(64ビットシステムでも)は、サイズが2GBを超える単一のオブジェクトを割り当てることはできません。 (確かにそうだったのです。)私の推測では、あなたのStringBuilderはサイズを2倍にしようとしていて、その限界を超えています。 StringBuilderを特定の容量(約10億の容量)で構築することで、少し高くなる可能性がありますmay実現可能です。

もちろん、通常の過程ではこれは問題ではありません。数百メガバイトを必要とする文字列でさえまれです。


1StringBuilderの実装は、実際には.NET 4で変更され、状況によってはフラグメントを使用するようになったと思いますが、詳細はわかりません。したがって、ビルダー形式のままで常に連続したメモリを必要としない場合があります...しかし、ToStringを呼び出した場合はそうなります。

33
Jon Skeet