Artykuł ten jest częścią serii artykułów na temat Garbage Collector-a.
Sprawdzanie kiedy sąsiad wyrzuca śmieci.
„Prawdziwy programista sam” wyrzuca śmieci.
Może by tak jednak zostawić bajzel i wyjechać w Bieszczady…
Garbage Collector ma zaimplementowaną pewną funkcjonalność. Daje nam ona kontrolę nad procesem niszczenia obiektów. Jako że do zarządzania pamięcią wykorzystywany jest specjalny agent i nie musimy się martwić o niszczenie obiektów. Tym samym nie wiemy, kiedy to nastąpi.
Użycie finalizatorów spowoduje możliwość uchwycenia procesu niszczenia obiektu w metodzie.
Działanie
Garbage Collector skanując obiekty przeznaczone do niszczenia, inaczej traktuje obiekty zawierające finalizator. Umieszcza je na specjalnej kolejce obiektów nieosiągalnych (ang. freachable quaque).
Zawartość kolejki jest przetwarzana, i proces niszczenia obiektu jest delegowany do metody: protected override void Finalize().
Zwalnianie pamięci w ten sposób jest czasochłonne, dlatego należy go używać z rozwagą i w zasadzie powinno się stosować tylko do specyficznych sytuacji:
- otwartych połączeń sieciowych,
- połączeń do bazy danych,
- otwartych plików,
- użytych bibliotek spoza .NET półświatka,
- obiektów COM (Component Object Model).
Finalizatory można używać jedynie w klasach i tylko jedno wystąpienie na klasę.
Finalizujemy jedynie obszary niezarządzanego kodu.
Jak użyć?
Szkielet metody Finalize.
protected override void Finalize()
{
try
{
// niszczymy obiekty!
}
finally
{
base.Finalize();
}
}
Możemy oczywiście użyć składni podobnej do jęcyka C++, czyli destruktora. Jednak będzie on przez kompilator zamieniony na metodę Finalize.
class NazwaKlasy
{
~NazwaKlasy() // destruktor taki jak w C++:)
{
// i czyścimy pamięć
}
}
Wykorzystanie interfejsu IDisposable
Do poprawnego użycia finalizatorów wykorzystamy interfejs IDisposable.
Poniżej znajduje się szkielet implementacji użycia IDisposable.
public class NazwaKlasy: IDisposable {
bool disposed = false;
//przydatna flaga,
//określająca czy Dispose zostało już wykonane
public void Dispose()
//metoda pochodzi z interfejsu IDisposable,
//możemy ją wołać samodzielnie z kodu.
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing) {
// niszczymy obiekty
}
// Niszczymy niezarządzane obiekty
disposed = true;
}
~NazwaKlasy() //finalizator
{
Dispose(false);
}
}
Metoda Dispose może zostać wywołana z obiektu. Jest ona także wymagana, by wykorzystać dyrektywę języka using.
using (var nazwaObiektu = new NazwaKlasy()) {
nazwaObiektu.Metoda();
nazwaObiektu.Metoda2();
}
W takiej składni metoda Dispose z interfejsu IDisposable zostanie wywołana przez dyrektywę using.
W przypadku braku implementacji interfejsu IDisposable przez klasę, będziemy mieli błąd kompilacji.
C.D.N.
UWAGA ZABAWA Z GARBAGE COLLECTORem NIESIE ZE SOBĄ POWAŻNE SKUTKI.
ZWŁASZCZA GDY SIĘ TO ROBI W ZŁY SPOSÓB! NIE JEST TO SILVER-BULLET NA KAŻDY PROBLEM.
I NALEŻY UWAŻAĆ. GDYŻ MOŻNA WIĘCEJ NAPSUĆ NIŻ NAPRAWIĆ;).
Referencje: