Absolutne minimum o Dispose i GC pt. 2
W poprzednim artykule opisałem zjawisko memory leak wywołane błędnym posługiwaniem się eventami w .NET. Tym razem zademonstruję jedną z dostępnych metod wyszukiwania w kodzie obiektów, które nie zostają usunięte przez Garbage Collector.
Autor:Wojciech Turowicz
Źródło:http://wtbi.pl/blog/post/Absolutne-minimum-o-Dispose-i-GC-pt-2.aspx/
Naszym laboratoryjnym przykładem będzie kod:
class Listener : IDisposable
{
public Listener()
{
SystemEvents.SessionSwitch
+= new SessionSwitchEventHandler(SystemEvents_SessionSwitch);
}
void SystemEvents_SessionSwitch(object s, SessionSwitchEventArgs e)
{
Console.WriteLine(e.Reason);
}
~Listener()
{
Console.WriteLine("kill");
}
public void Dispose()
{
Console.WriteLine("dispose");
}
}
static class Program
{
static void Main(string[] args)
{
Console.WriteLine("Przed operacja");
Console.ReadLine();
CreateListeners();
GC.Collect();
Console.WriteLine("Po operacji");
Console.ReadLine();
}
private static void CreateListeners()
{
for (int i = 0; i < 100; i++)
new Listener();
}
}
Jak widać w pętli tworzymy 100 obiektów typu Listener, które w konstruktorze rejestrują się w zdarzeniu systemowym SessionChanged. Zdarzenie jest statyczne, dlatego obiekty nie powinny zostać usunięte aż do końca działania programu. Specjalnie wywołuję GC.Collect() aby wymusić zbieranie obiektów przez Garbage Collector na wypadek gdyby nie zdążył się uruchomić. Pamiętaj, że GC.Collect dla dużej ilości obiektów może długo trwać, dlatego należy posługiwać się generacjami.
Po uruchomieniu programu faktycznie widać, że GC nie wywołuje Destruktora naszych obiektów. Inaczej dzieje się w przypadku zakomentowania konstruktora klasy Listener.
Ale jak stwierdzic co się nie zwalnia w dużej aplikacji?
Po pierwsze należy mieć pewne podejrzenia co do miejsc programu, w których może dochodzić do Memory Leaków. Można to wykrywać np. za pomocą Process Hackera albo Menedżera Zadań podglądając ilość zajętej pamięci, handli GDI i USER.
To, co teraz zamierzamy zrobić to tzw. „Memory Profiling” – pomożemy sobie narzędziem Jetbrains DotTrace. Aby być gotowym należy skompilować naszą aplikację, uruchomić narzędzie DT i kliknąć „Profile Application…”. Wybieramy ścieżkę do naszej aplikacji i tryb działania „Memory Profiling”:
Po kliknięciu „Start Application” pojawi się okienko kontrolne:
Wybieramy „Mark Memory”
Klikamy w naszej aplikacji, żeby przetworzyła operacje.
Wybieramy „Get Snapshot”
Pojawia się okno profilera, które nic konkretnego na pierwszy rzut oka nie mówi:
Z lewego menu wybieramy „Namespace Tree”, a u góry ikonę trupich czach:
Teraz widać jaka jest nadwyżka obiektów po wykonanej operacji. Czerwony napis „+100″ informuje nas o tym, że dana operacja utworzyła 100 obiektów w pamięci, które nadal żyją.
Do powyższej demonstracji nie trzeba już chyba nic dodawać. GC ma wiele zalet ale trzeba umieć z nim żyć. Jeżeli znasz jakiś dobry darmowy Memory Profiler to proszę daj mi o nim znać :)


(3 głosów, średnia: 4,67 / 5)










Zostaw odpowiedź