6

오늘 웹 서버에서 메모리가 매우 빠르게 사라지는 생산 환경에서 우리는 큰 파급을 경험했습니다. 이것은 Ninject의 캐싱 메커니즘으로 거슬러 올라갔습니다 (Activation Cache 또는 뭔가라고 생각합니다 - 완전히 확신하지 못했습니다). 이 문제를 조사한 결과 범위 콜백에서 순환 참조가 있다는 결론에 도달했습니다.순환 범위 콜백 참조로 인한 메모리 누출 문제가 발생했습니다.

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

이것은 문제의 원인이 된 코드를 나타냅니다. 겉으로는 CookieContainer의 범위 콜백이 HttpContext.Current 였고 HttpContext.Current가 CookieContainer에 의해 참조되고있었습니다. 따라서 Ninject는 CookieContainer 인스턴스를 캐시에서 제거 할 수 없으며 CookieContainer 인스턴스는 범위 콜백 객체를 유지합니다. CookieContainer의 범위를 일시적으로 변경하면 예상대로 작동합니다. 그러나 이것이 올바르게 행해질 수있는 상당히 일반적인 것 같아서 왜 이런 일이 일어 났는지 아직도 확실하지 않습니다. 어쩌면 아마도 ...

콜백 개체가 그대로 살아 있다면 콜백이 여전히있는 것처럼 보면서 캐시에서 같은 인스턴스를 다시 돌려 보내면 안됩니다. 살아있는, 그래서 인스턴스가 범위에 있어야 나타납니다? 왜 ninject가 계속해서 CookieContainer의 새로운 인스턴스를 가져 와서 캐싱을 수행할까요? 나는 잘못된 객체와 관련된 다른 문제가있을 것이라고 생각하지만 적어도 메모리 누수가 아닌 버그 일뿐입니다.

제 질문은 a)이 오류를 올바르게 진단 했습니까? b)이 문제가 다시 발생하지 않도록하기 위해 권장되는 접근법이 있습니까? c)이 유형의 순환 의존성을 검사하기 위해 코드에 수정을 적용 할 수 있습니까?

답변

7

간단히 설명하면 캐시는 인스턴스에 대해 약한 참조 범위 개체의 사전입니다. 스코프가 살아있는 한, 참조 된 객체도 살아있게 유지됩니다. 그래서 네 CookieContainer가 HttpContext.Current를 참조하고 요청 범위에 있다면이 표준 메커니즘은 결코 그들을 풀어주기 위해 적용되지 않을 것입니다.

그러나 InRequestScope의 특별한 경우에는 OnePerRequestModule에 의해 구현되는 다른 해제 메카니즘이 있으며 요청이 완료된 직후 모든 InRequestScoped 객체를 해제합니다. Ninject.Web 또는 Ninject.Web.MVC3의 최신 버전을 사용하는 경우 사전 구성되어 있습니다. 그렇지 않으면 web.config에서이 HTTPModule을 구성하여 명시 적으로 추가해야합니다.

당신이 이해하지 못했던 다른 점은 Ninject가 살아있을 때 동일한 객체를 반환하지 않는다는 것입니다. 예 : 요청 범위에서 요청에 대해 동일한 객체를 반환합니다. 여러 요청이 동시에 실행되는 경우 모두 다른 인스턴스가 생성됩니다.

+0

오늘 내 엉덩이를 구한 사람들. 우리에게는 똑같은 문제가있었습니다! – pkolodziej