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 源中的 Container.cs (最后一次更新是在 2011 年),我可以看出它泄漏了子容器。

CreateChildContainer 方法创建新容器,将其与父容器连接,并将创建的引用添加到 childContainers 堆栈。

只有两个地方用到了 childContainers 堆栈:

  • childContainers.Push(child); 在 Container.CreateChildContainer() 中(第 73 行)

  • childContainers.Pop().Dispose(); 在 Container.Dispose() 中(第 88 行)

因此,如果您创建子容器,然后释放它(但不是其父容器) - 对已释放子容器的引用保留在父容器中,因为没有清理代码将从父堆栈中删除已释放引用。

可能您可以通过创建代理子容器(仅一次),然后从中派生所有真正的子容器来解决此问题。由于 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

这个问题是由于父容器有一个子容器的引用,即使子容器被释放。垃圾收集器适用于大多数情况,但我认为这是极端情况。最简单的方法是,如果容器父级不为空,我们从父级的子级集合中删除容器引用。

您可以查看此单元测试(您的代码片段)及其实现的方式。

于 2014-01-15T12:50:28.350 回答