2013-03-19 52 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中查看路線。