6

Tuvimos una interrupción importante hoy en producción donde la memoria estaba desapareciendo muy rápidamente de nuestros servidores web. Esto se remontaba a un mecanismo de caché en Ninject (creo que era Caché de Activación o algo así, no del todo seguro). Después de investigar el problema, llegamos a la conclusión de que teníamos una referencia circular en nuestra devolución de llamada de alcance.fuga de memoria ninject debido a la referencia de devolución de llamada de alcance circular

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

Esto es una representación de nuestro código que causaba el problema. Al parecer, lo que sucedió fue que la devolución de llamada del alcance para CookieContainer era HttpContext.Current, y HttpContext.Current también estaba siendo referenciado por CookieContainer. Así que Ninject nunca podría eliminar las instancias de CookieContainer de su caché, ya que las instancias de CookieContainer mantienen vivos sus objetos de devolución de llamada de alcance. Cuando cambiamos el alcance de CookieContainer a transitorio, todo funciona bien, como esperábamos. Pero todavía no estoy totalmente seguro de por qué sucedió esto, ya que parece que esto es algo bastante convencional de hacer, ¿verdad? Tal vez quizás no ...

También estoy confundido ya que pensé que si el objeto de devolución de llamada se mantenía vivo como lo hizo, no debería Ninject devolver la misma instancia de la memoria caché, viendo como si la devolución de llamada todavía estuviera vivo, entonces la instancia debería aparecer en el alcance? ¿Por qué ninject seguiría obteniendo nuevas instancias de CookieContainer y almacenándolos en caché? Supongo que habría otros problemas relacionados con la devolución del objeto incorrecto, pero eso sería al menos un error, no una pérdida de memoria.

Mi pregunta es a) ¿hemos diagnosticado correctamente este error? b) ¿hay un enfoque recomendado para que esto no vuelva a suceder? c) ¿Puedo poner una corrección en el código para verificar este tipo de dependencia circular (suponiendo que hayamos diagnosticado esto correctamente)?

Respuesta

7

Simplemente descrito, el caché es un diccionario del objeto de alcance de referencia débil para la instancia. Mientras el alcance esté vivo, los objetos referenciados también se mantendrán vivos. Así que sí, si su CookieContainer hace referencia a HttpContext.Current y está dentro del alcance de la solicitud, este mecanismo estándar nunca se aplicará para liberarlos.

Pero en el caso especial de InRequestScope hay otro macanismo de liberación implementado por el OnePerRequestModule que liberará todo el objeto InRequestScoped inmediatamente después de que se complete la solicitud. Si está utilizando una versión actualizada de Ninject.Web o Ninject.Web.MVC3, está preconfigurada. De lo contrario, debe agregarlo explícitamente configurando este HTTPModule en web.config.

El otro punto que no entendiste es que Ninject no devolverá el mismo objeto mientras viva. P.ej. en el alcance de la solicitud devolverá el mismo objeto para una solicitud. Si se ejecutan varias solicitudes al mismo tiempo, todas obtienen una instancia diferente.

+0

Chicos, me salvaste el culo hoy. ¡Tuvimos exactamente el mismo problema! – pkolodziej

Cuestiones relacionadas