Projektowanie, Programowanie, Codzienność – BeniaminZaborski.com

19 Grudzień 2014

Castle Windsor – Transient Memory Leak

Filed under: How Can I Take ... — Tags: , , , , , — Beniamin Zaborski @ 22:20

Od jakiegoś czasu Castle Windsor to mój ulubiony kontener IoC. Jutro co prawda może się nim stać Ninject lub Autofac, ale nie dlatego że mi źle z Windsor, a raczej z czystej ciekawości. Przejdźmy do rzeczy.
Jak generalnie wyobrażamy sobie pracę z kontenerem IoC? Z reguły są dwa kroki do wykonania: rejestracja typu i pobranie instancji tego typu. W Windsorze w najprostszej formie wygląda to tak:

Rejestracja:

container.Register(
   Component.For<ISecurityService>()
   .ImplementedBy<SecurityService>()
);

Pobranie:

ISecurityService securityService = container.Resolve<ISecurityService>();

Niby wszystko jest OK i jest to jakaś alternatywa dla tego:

ISecurityService securityService = new SecurityService();

No tak, ale ktoś mógłby spytać o długość życia obiektu securityService. I w tym momencie dotykamy bardzo ważnej kwestii kontenerów IoC – LifeStyle. Każdy kontener IoC, Windsor także, posiada kilka wbudowanych elementarnych LifeStyle’i tj.: Singleton, Transient, Scope, PerWebRequest, itd.
LifeStyle określa zasięg obiektów instancjonowanych przez kontener oraz mówi kiedy będą niszczone. Singleton jest domyślnym lifestyle’m w Windsorze. Oznacza to, że będzie wytworzona jedna instancja przy pierwszym wywołaniu Resolve i reużywana przy kolejnych wywołaniach Resolve.
Zupełnym przeciwieństwem do Singletona jest Transient – przy każdym wywołaniu Resolve kontener zwraca nową instancję.

Rejestracja naszego serwisu dla Transient wyglądałaby tak:

container.Register(
   Component.For<ISecurityService>()
  .ImplementedBy<SecurityService>()
   .LifeStyle.Transient
);

Pobieramy tak zarejestrowany serwis, robimy z nim co mamy do zrobienia i zapominamy … Garbage Collector go zniszczy. Fałsz! Mina! Memory leak! Nic takiego nie nastąpi dopóki nie zniszczymy naszego kontenera IoC (jeśli w ogóle to robimy?). To jest dość często spotykana mina na którą się można nadziać. Windsor może trzymać referencję do obiektu transient, a więc Garbage Collector nie będzie w stanie zwolnić zasobów. Dla wszystkich obiektów transient najwyższego rzędu które zostaną przez nas jawnie pobrane z kontenera IoC musimy wykonać Release. Poprawna sekwencja powinna wyglądać tak:

Rejestracja:

container.Register(
   Component.For<ISecurityService>()
   .ImplementedBy<SecurityService>()
   .LifeStyle.Transient
);

Pobranie:

ISecurityService securityService = container.Resolve<ISecurityService>();

Praca z serwisem:

securityService.Authenticate(authDTO);

Zwolnienie:

container.Release(securityService);

I tak sobie myślę, że potencjalnie wbrew pozorom najbardziej zagrożeni nadzianiem się na tę minę nie są początkujący użytkownicy kontenerów IoC (jest szansa że oni uważnie czytają dokumentację), a raczej tacy którzy przesiadają się na Windsora z jakiś „starszych” rozwiązań, myśląc że to tylko kolejny kontener z nieco innym API. Mea culpa, przyznaję się, kiedyś sam się nadziałem, ale widzę że nie jestem sam ;).

Dodaj komentarz »

Brak komentarzy.

RSS feed for comments on this post. TrackBack URI

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

Blog na WordPress.com.

%d bloggers like this: