4

Windows サービスで Funq を使用していくつかのスケジュールされたタスクを実行しています。各ラウンドでは、すべてのオブジェクトを作成するよりも子コンテナーを作成しており、最後に子コンテナーを破棄しています。この子コンテナーによって作成された要素は GC ではないことがわかりました。ルート コンテナには、子コンテナの破棄を呼び出した後もそこにとどまる子コンテナのコレクションがあります。このコードは問題を再現し、800MB のメモリを消費 (および保持) します。

私たちにとっては非常に驚きました。このように funq を使用するのは間違ったパターンなのでしょうか?この場合、どのように使用すればよいのでしょうか? それとも単なるバグですか?

ありがとう

public class Dummy
{
    public string Content { get; set; }
    public void Generate(int size)
    {
        this.Content = new string('X', size);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var container = new Container();
        container.RegisterAutoWired<Dummy>().ReusedWithin(ReuseScope.Container);
        int size = 20000;
        for (int i = 0; i < size; i++)
        {
            using (var c = container.CreateChildContainer())
            {
                var d= c.Resolve<Dummy>();
                d.Generate(size);
            }
            PrintInfo(i);
        }

        Console.ReadLine();
    }

    private static void PrintInfo(int i)
    {
        if (i%1000 == 0)
        {
            int divide = 1024*1024;
            GC.Collect();
            var p = System.Diagnostics.Process.GetCurrentProcess();
            Console.WriteLine(p.WorkingSet64/divide + "MB");
            Console.WriteLine(p.PrivateMemorySize64/divide + "MB");
        }
    }
}
4

2 に答える 2

3

Funq ソース(最後に更新されたのは 2011 年) のContainer.cs を見ると、子コンテナーがリークしていることがわかります。

CreateChildContainer メソッドは新しいコンテナーを作成し、それを親コンテナーに接続し、作成された参照を childContainers スタックに追加します。

childContainers スタックが使用される場所は 2 つだけです。

  • childContainers.Push(子); Container.CreateChildContainer() 内 (73 行目)

  • childContainers.Pop().Dispose(); Container.Dispose() で (88 行目)

そのため、子コンテナーを作成して破棄すると (親ではなく)、親のスタックから破棄された参照を削除するクリーンアップ コードがないため、破棄された子への参照は親に残ります。

おそらく、プロキシの子コンテナーを作成し (1 回だけ)、そこからすべての実際の子コンテナーを派生させることで、これを回避できます。Dispose メソッドはオブジェクトを使用不可状態に移行しないため、プロキシの子に対して Dispose を何度も呼び出すことで、すべての子をクリーンアップできます。

    var container = new Container();
    container.RegisterAutoWired<Dummy>().ReusedWithin(ReuseScope.Container);
    int size = 20000;
    var proxy = container.CreateChildContainer()
    for (int i = 0; i < size; i++)
    {
        using (proxy)
        using (var c = proxy.CreateChildContainer())
        {
            var d= c.Resolve<Dummy>();
            d.Generate(size);
        }
        PrintInfo(i);
    }
于 2013-03-24T19:00:41.863 に答える
0

この問題は、子コンテナーが破棄された場合でも、親コンテナーが子コンテナーの参照を持っていることが原因です。ガベージ コレクターはほとんどのシナリオでうまく機能しますが、これはまれなケースだと思います。最も簡単な方法は、コンテナーの親が null でない場合、親の子コレクションからコンテナー参照を削除することです。

この単体テスト(コード スニペット) とその実装から方法を確認できます。

于 2014-01-15T12:50:28.350 に答える