17

リークをチェックするために、メモリプロファイラーを介してアプリケーションを実行しています。物事はある程度問題ないように見えますが、ファイナライザーキューで何もしていないように見えるこれらのOverlappedDataがたくさんあります。NetworkStreamこれらは、接続のいずれかの端で基盤となるものをシャットダウンすることによってキャンセルされた、重複したIOの結果です。

ネットワークストリーム自体は破棄されます。どこにもライブインスタンスはありませんNetworkStream

通常、それらはと呼ばれるものに根ざしています。私が最初に行うことはコールバックでOverlappedDataCacheLine呼び出しているので、対応するがなければへの呼び出しはありません。EndReadBeginReadEndRead

これは、ツールからそれを保持している人のかなり典型的な外観です

OverlappedDataは助けになっています

結局、それGCされますが、それは永遠にかかります-私が約1000のストリームを開始したときにすべてを殺すのに30分ほどかかり、それらを非同期呼び出しに入れ、BeginRead約1分後にシャットダウンします。

このプログラムは、ポート80のWebサーバーに対して問題をいくらか再現します。どのWebサーバーでも実際に実行できます。

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        var clients = new List<TcpClient>();
        for (int i = 0; i < 1000; ++i) {
            var client = new TcpClient();
            clients.Add(client);

            client.BeginConnect("localhost", 80, connectResult =>
                {
                    client.EndConnect(connectResult);

                    var stream = client.GetStream();
                    stream.BeginRead(new byte[1000], 0, 1000, result =>
                        {
                            try
                            {
                                stream.EndRead(result);
                                Console.WriteLine("Finished (should not happen)");
                            }
                            catch
                            {
                                // Expect to find an IO exception here
                                Console.WriteLine("Faulted");                               
                            }
                        }, stream);     
                }, client);             
        }

        Thread.Sleep(10000); // Make sure everything has time to connect

        foreach (var tcpClient in clients)
        {
            tcpClient.GetStream().Close();
            tcpClient.Close();
        }
        clients.Clear(); // Make sure the entire list can be GC'd

        Thread.Sleep(Timeout.Infinite); // Wait forever. See in profiler to see the overlapped IO take forever to go away
    }
}

OverlappedData確かに、このプログラムは実際のアプリケーションよりもはるかに小さいため、千をクリアするのに永遠にかかることはありませんが、その処理には時間がかかります。このテストアプリケーションの代わりに実際のものを実行すると、ファイナライザーがスタックしているという警告が表示されます。私のアプリケーションではあまり効果がなく、閉じられていない可能性のあるものをすべて閉じようとし、どこにも参照が保持されていないことを確認します。

Dispose()私が電話をかけたのかClose()、クライアントに電話をかけたのか、それがストリームであるのかは、まったく問題ではないようです。結果は同じです。

なぜこれが起こるのか、そしてこれを回避する方法についての手がかりはありますか?CLRは私にとって賢く、新しい呼び出しに備えて、これらの固定されたメモリブロックをそのまま維持しているのでしょうか。そして、なぜファイナライザーの完了が非常に遅いのですか?

更新F5キーにコップ一杯の水を入れてコーヒーを飲んで、信じられないほど愚かな負荷テストを行った後、何かがこれらのものを収集するストレス下でより完全なGCをトリガーするようです。したがって、実際には実際の問題はないように見えますが、実際にここで何が起こっているのか、なぜこのオブジェクトの収集が他のオブジェクトよりも大幅に遅いのか、そしてこれが断片化された後の段階で問題になる可能性があるのか​​を知ることは素晴らしいことですメモリなど。

4

2 に答える 2

6

さて、今何が起こっているのかは明らかなようです。ガベージコレクションは、メモリを割り当てたときにのみ発生します。少なくとも2メガバイトの割り当てが必要です。これは、GCをトリガーするための第0世代のGCヒープの一般的な初期サイズです。つまり、何も実行しないプログラムはGCを実行することはなく、メモリプロファイラーを使用してヒープにまだ収集されていないオブジェクトが長期間表示されます。

これはあなたが説明していることの良い説明です。すべての接続を終了すると、プログラムはそれ以上何もする必要がなくなります。したがって、メモリがあればあまり割り当てないので、コレクションはトリガーされません。プロファイラーにコレクションが表示されない場合は、Perfmon.exeでコレクションを表示できます。それ以外の場合、これはまったく問題ではなく、ガベージコレクターの動作の副作用にすぎません。

プログラムにリソース消費の問題が暴走しているという明確な証拠がある場合にのみ、リークについて心配する必要があります。

于 2012-09-06T13:23:51.763 に答える
0

AsyncResultラムダクロージャの問題を除外するために、からクライアントを取得してみてください。

TcpClient t = (TcpClient)connectResult.AsyncState;

EndConnect また、処理を終了するために後で電話するべきではありませんか?

于 2012-09-06T09:47:14.457 に答える