由于循环范围回调引用而导致内存泄漏

Aar*_*nHS 7 c# dependency-injection ninject inversion-of-control circular-reference

今天我们在生产中发生了重大中断,内存从我们的网络服务器很快就消失了.这可以追溯到Ninject中的缓存机制(我认为它是激活缓存或其他东西 - 不完全确定).在调查问题之后,我们得出结论,我们在范围回调中有一个循环引用.

class View
{
    Presenter presenter;

    View()
    {
        //service locators are smelly, but webforms forces this uglyness
        this.presenter = ServiceLocator.Get<Func<View, Presenter>>()(this);

        this.presenter.InitUI();
    }
}

class Presenter
{
    CookieContainer cookieContainer;
    View view;

    Presenter(View view, CookieContainer cookieContainer)
    {
        this.view = view;
        this.cookieContainer = cookieContainer;
    }
}

class CookieContainer
{
    HttpRequest request;
    HttpResponse response; 

    CookieContainer()
    {
        this.request = HttpRequest.Current.Request;
        this.response = HttpRequest.Current.Response;
    }
}

Bind<Func<View, Presenter>>().ToMethod(ctx => view => 
        ctx.Kernel.Get<Presenter>(new ConstructorArgument("view", view)));

Bind<Presenter>().ToSelf().InTransientScope();
Bind<CookieContainer>().ToSelf().InRequestScope();
Run Code Online (Sandbox Code Playgroud)

这是导致问题的代码的表示.看起来发生了什么,CookieContainer的范围回调是HttpContext.Current,而CookieContainer也引用了HttpContext.Current.所以Ninject永远不会从其缓存中删除CookieContainer实例,因为CookieContainer实例保持其范围回调对象的活动.当我们将CookieContainer的范围更改为瞬态时,一切正常,正如我们预期的那样.但我仍然不能完全确定为什么会发生这种情况,因为看起来这是一个相当传统的做法呢?也许不是......

我也很困惑,因为我认为如果回调对象保持活着状态,那么Ninject不应该只从缓存中移回相同的实例,看到回调仍然存在,所以实例看起来应该是范围?为什么ninject会不断获取CookieContainer的新实例并缓存它们?我想还有其他问题与不正确的对象有关,但这至少只是一个错误,而不是内存泄漏.

我的问题是a)我们是否正确诊断出此错误?b)是否有建议的方法来避免再次发生这种情况?c)我可以在代码中加入修复以检查这种类型的循环依赖性(假设我们已经正确诊断了这个)吗?

Rem*_*oor 8

简单地说,缓存是实例的弱引用范围对象的字典.只要范围是活动的,引用的对象也会保持活动状态.所以,如果你的CookieContainer引用了HttpContext.Current并且在请求范围内,那么这个标准机制永远不会适用于释放它们.

但在InRequestScope的特殊情况下,OnePerRequestModule实现了另一个释放机制,它将在请求完成后立即释放所有InRequestScoped对象.如果您使用的是Ninject.Web或Ninject.Web.MVC3的最新版本,则它已预先配置.否则,您必须通过在web.config中配置此HTTPModule来显式添加它.

你不明白的另一点是Ninject只要它存在就不会返回同一个对象.例如,在请求范围内,它将为请求返回相同的对象.如果多个请求同时运行,则它们都会获得不同的实例.

  • 伙计们,你们今天救了我的命。我们遇到了完全相同的问题! (2认同)