Funq和处理儿童容器

时间:2013-03-19 22:50:06

标签: .net garbage-collection servicestack funq

我们在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");
        }
    }
}

2 个答案:

答案 0 :(得分:3)

通过查看Funq sources中的Container.cs(2011年最后一次更新), 我可以说它泄漏了儿童容器。

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

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

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

  • childContainers.Pop()配置()。在Container.Dispose()(第88行)

因此,如果您创建子容器,然后处置它(但不是它的父容器) - 由于没有清理代码,因此对被处置子女的引用保留在父母中 这将从父母的堆栈中删除已处置的引用。

可能你可以通过创建代理子容器(只有一次)来解决这个问题, 然后从中导出所有真正的子容器。既然Dispose方法呢 不将对象转移到无法使用状态 - 您可以清理所有孩子 通过一遍又一遍地调用Dispose for proxy child:

    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);
    }

答案 1 :(得分:0)

此问题是由于父容器具有子容器的引用,即使子容器已被丢弃也是如此。垃圾收集器适用于大多数情况,但我认为这是最坏的情况。最简单的方法是,如果容器父项不为null,则从父项的子集合中删除容器引用。

您可以查看this unit test(您的代码段)和its implementation的方式。