我们在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");
}
}
}
答案 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的方式。