2013-03-19 47 views
4

我们在我们的Windows服务中使用Funq来执行一些计划任务,并且对于每一轮我们都创建一个子容器,而不是创建所有对象,并在最终处置子容器时发现元素由此子容器创建的不是GC,因为根容器具​​有在调用处理子容器后停留在那里的子容器的集合。这段代码再现了我们的问题,并将消耗(并保持)800MB的内存。Funq和处理子容器

对我们来说,这是相当令人惊讶的,这是用这种方式使用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"); 
     } 
    } 
} 

回答

3

Funq sources看着Container.cs(这是过去的2011年更新), 我可以告诉大家,它泄漏子容器。

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

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

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

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

所以,如果你创建子容器,然后处理它(但不是其父) - 引用到布置孩子留在父母,因为没有清理代码 这将从父代堆栈中移除已处理的引用。

也许你可以通过创建代理子容器(仅一次), ,然后从中获取所有真实的子容器来解决此问题。

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

此问题是由于父容器具有这样的事实:通过调用Dispose为代理的孩子,一遍又一遍,你可以清理所有儿童 - 由于Dispose方法不 不转让对象变成不可用状态即使儿童容器被丢弃,也是子容器的参考。垃圾收集器适用于大多数场景,但我认为这是个案例。最简单的方法是,如果容器父项不为空,我们会从父项的子集合中移除容器引用。

您可以从this unit test(您的代码段)和its implementation中查看路线。