-1

を使用してネットワーク経由でファイルを送受信するプログラムで作業していますTCP。プログラムは複数のファイルを送信するため、ユーザーがプログラムを終了するまでストリームは閉じられません。

私が直面している問題は、700 MB のファイルを送信すると、サーバー プログラムのプライベート メモリが 700,000 K にまで増大し、コンピューターのパフォーマンスが著しく低下することです。また、別の 700 MB ファイルを送信しようとすると、サーバーはSystem.OutOfMemoryException.

誰かが私が間違っていること、またはしていないことを教えてもらえますか?

サーバー側コード:

   using (   FileStream fs = new FileStream("dracula.avi", FileMode.Open, FileAccess.Read))                            
  {                                                        
   byte[] data = new byte[fs.Length];
   int remaining = data.Length;
   int offset = 0;

   strWriter.WriteLine("Content-Length: " + data.Length);
   strWriter.Flush();

   Thread.Sleep(1000);
   while (remaining > 0)
        {
            Thread.Sleep(10);
            int read = fs.Read(data, offset, remaining);
            remaining -= read;
            offset += read;

        }

   fs.Flush();
   fs.Close();
 }   

  strm.Write(data, 0, data.Length);
  strm.Flush();
  GC.Collect();
4

4 に答える 4

4

別のストリームにコピーしたいだけなのに、現在ファイル全体をメモリに読み込んでいます。そうしないでください。一度にチャンクを繰り返すだけです: チャンクの読み取り、チャンクの書き込み、チャンクの読み取り、チャンクの書き込みなど。.NET 4 を使用している場合Stream.CopyToは、その目的で使用できます。

于 2012-11-25T19:13:11.827 に答える
3

読み取りをバッファリングしていますが、書き込みはバッファリングしていません。プログラムは、まさにあなたが指示したことを実行しています。つまり、巨大なチャンクまたはメモリを割り当て、1 バイトを送信する前にすべてを埋めます。

はるかに優れた方法は、ファイルから小さなチャンク (議論のために 4096 バイト) を読み取り、そのチャンクを出力ストリームに書き込むことです。これにより、接続ごとに 4096 バイトしか使用できず、スケーラビリティが大幅に向上します。

于 2012-11-25T19:13:22.097 に答える
1

通常、OOM 状態は、システム メモリが不足している場合、または 32 ビット プロセスでアドレス空間 (2000MB) が不足している場合に発生します。

1つはコピーできますが、2つはコピーできないと言いますか?それは2つ同時にですか、それとも連続ですか?スレッド モデルは何ですか? また、例はスニペットです。書き込み用の StreamWriter と Stream があるようですが、これらのオブジェクトはなくなりますか?

GC.Collect には注意してください。明示的な呼び出しを正しく使用しないと、オブジェクトが必要以上に長く存続する可能性があるため、Microsoft は明示的な呼び出しをお勧めしません。これは、GC.Collect を実行すると、オブジェクトがより高い世代に昇格するためです。私の経験では、オブジェクトを解放していることを確認し、GC.Collect の内容とタイミングをフレームワークに決定させるのが最善です。

WinDBG+SOS に慣れると、ヒープ上のオブジェクトを確認できるようになります。

これを試して:

  1. WinDBG を起動し、プロセスにアタッチします
  2. 4.0 を使用している場合は「.loadby sos clr」と入力し、それ以外の場合は「.loadby sos mscorwks」と入力します。
  3. F5 を押して続行します
  4. 1 つのファイルをコピーし、完了するまで待ちます
  5. CTRL+BREAK を押します
  6. 「!dumpheap -stat」と入力し、結果を見て、削除すべきオブジェクトを探します。
  7. なくなるはずの For-Each オブジェクトで、MT 値を取得します
  8. 「!dumpheap -mt {0}」と入力して、{0} を上記の手順の値に置き換えます
  9. これはインスタンスのリストです。オブジェクト アドレスの 1 つを取得してください
  10. 「!gcroot {0}」と入力して、{0} をオブジェクトのアドレスに置き換えます

これにより、何がオブジェクトをルート化しているかがわかります。その後、不要な null オブジェクトなど、ルート化を解除する方法を見つける必要があります。

于 2012-11-25T20:08:37.853 に答える
1

データチャンクを読み取ったらすぐに送信することをお勧めします。私はコードをテストしませんでしたが、次のようなものになるはずです。

   var bufferLenght = 1024;
   byte[] buffer;

   while (remaining > 0)
   {
            buffer = new byte[1024];
            int len = fs.Read(buffer, offset, bufferLenght);
            remaining -= len;
            offset += len;    
            strm.Write(buffer, 0, len);
   }
于 2012-11-25T19:28:49.357 に答える