5

File.AppendAllText最初にメモリ ストリームに書き込み、次にファイルにコピーするのではなく、ログ記録がどのように実行されるかを確認するために、いくつかのテストを実行していました。だから、メモリ操作がどれほど速いかを見るために、これをやった..

private void button1_Click(object sender, EventArgs e)
    {
        using (var memFile = new System.IO.MemoryStream())
        {
            using (var bw = new System.IO.BinaryWriter(memFile))
            {
                for (int i = 0; i < Int32.MaxValue; i++)
                {
                    bw.Write(i.ToString() + Environment.NewLine);
                }
                bw.Flush();
            }
            memFile.CopyTo(new System.IO.FileStream(System.IO.Path.Combine("C", "memWriteWithBinaryTest.log"), System.IO.FileMode.OpenOrCreate));
        }
    }

i到達する25413324Exception of type 'System.OutOfMemoryException' was thrown.Process Explorerには約 700Mb の空き RAM があると表示されますが、

ここにスクリーンショットがあります(念のため)

プロセス エクスプローラー ここに画像の説明を入力

これがウィンフォームです

ここに画像の説明を入力

編集:ヒープ上に作成されるオブジェクトを増やすために、これに書き直しましbw.write

bw.Write(i);
4

3 に答える 3

9

まず、データをMemoryStreamに直接書き込むのではなく、 に蓄積するため、メモリが不足しますFileStream。を直接使用するFileStreamと、RAM はまったく必要ありません (ただし、ファイルを開いたままにしておく必要があります)。

未使用の物理メモリの量は、奇妙に聞こえるかもしれませんが、この例外とは直接関係ありません。

重要なのは:

  • プロセスの仮想アドレス空間で使用可能なメモリの連続したチャンクがあること
  • システム コミットが合計 RAM サイズ + ページ ファイル サイズを超えないこと

Windows のメモリ マネージャーに RAM の割り当てを依頼する場合、使用可能な量ではなく、他のすべてのプロセスで利用できるように約束されている量を確認する必要があります。このような約束は、コミットを通じて行われます。一部のメモリをコミットするということは、メモリ マネージャが、最終的にそれを使用するときに使用可能になるという保証を提供したことを意味します。

そのため、物理 RAM が完全に使い果たされている可能性がありますが、割り当て要求は引き続き成功します。なんで?ページファイルに利用可能なスペースがたくさんあるためです。このような割り当てによって取得した RAM を実際に使用し始めると、メモリ マネージャーは単純に別のものをページ アウトします。したがって、0 の物理 RAM != 割り当ては失敗します。

逆のことも起こりえます。未使用の物理 RAM があるにもかかわらず、割り当てが失敗する可能性があります。プロセスは、いわゆる仮想アドレス空間を通じてメモリを認識します。プロセスがアドレスでメモリを読み取る場合0x12340000、それは仮想アドレスです。0x78650000、または0x000000AB12340000(64 ビット OS で 32 ビット プロセスを実行している)でRAM にマップされる可能性があり、ページ ファイルにのみ存在するものを指す場合もあれば、何も指さない場合もあります。

連続したアドレスを持つメモリ ブロックを割り当てたい場合、RAM が連続している必要があるのはこの仮想アドレス空間です。32 ビット プロセスの場合、使用可能なアドレス空間は 2GB または 3GB しか得られないため、空き物理 RAM と十分な空き容量があるにもかかわらず、十分なサイズの連続したチャンクが存在しないような方法でそれを使い果たすことはそれほど難しくありません。未使用の仮想アドレス空間の合計。

于 2013-03-28T11:04:12.167 に答える
3

これは、メモリの断片化が原因である可能性があります。

大きなオブジェクトは大きなオブジェクト ヒープに移動し、移動してスペースを確保することはありません。これにより、使用可能なメモリにギャップがある場合に断片化が発生する可能性があり、使用可能なメモリのどのブロックよりも大きなオブジェクトを割り当てようとすると、メモリ不足が発生する可能性があります。

詳しくはこちらをご覧ください

しきい値が 1000 double (または 8000 バイト) である double の配列を除いて、85,000 バイトを超えるオブジェクトはすべてラージ オブジェクト ヒープに配置されます。

また、32 ビットの .Net プログラムは、オブジェクトごとに最大 2GB に制限されており、全体で 4GB よりやや少ない (OS によっては 3GB 程度) に制限されていることにも注意してください。

于 2013-03-28T10:59:08.110 に答える
0

BinaryWriterを使用してテキストをファイルに書き込むべきではありません。TextWriter代わりにaを使用してください。

今、あなたは使用しています:

for (int i = 0; i < Int32.MaxValue; i++)

これにより、書き込みごとに少なくとも 3 バイトが書き込まれます (数値表現と改行)。それまでInt32.MaxValueに、少なくとも 6 GB のメモリが必要であり、MemoryStream.

コードをさらに見てみるとMemoryStream、何らかの方法で をファイルに書き込むことになります。したがって、次のことを簡単に実行できます。

for (int i = 0; i < int.MaxValue; i++)
{
  File.AppendAllText("filename.log", i.ToString() + Environment.Newline);
}

または open に書き込みますTextWriter

TextWriter writer = File.AppendText("filename.log");

for (int i = 0; i < int.MaxValue; i++)
{
  writer.WriteLine(i);
}

クラッシュ時に書き込みの最後のビットが失われるため、IMO をログに記録するのは悪い考えであるメモリ バッファが必要な場合は、次を使用して を作成できますTextWriter

StreamWriter(string path, bool append, Encoding encoding, int bufferSize)

に「大きな」数値を渡しbufferSizeます。デフォルトは です1024

質問に答えるために、MemoryStreamサイズ変更のためにメモリ不足の例外が発生し、ある時点でメモリに収まらないほど大きくなります(別の回答で説明されています)。

于 2013-03-28T11:11:49.920 に答える