Może temat trochę na wyrost, ale już wyjaśniam o co chodzi. Czym jest że tak teoria spiskowa w .NET. Dla mnie jest to dziwne przeświadczenie dużej liczby programistów, że skoro w .NET istnieje Garbage Collector to pojęcie Memory Leaków nie istnieje. Nie chodzmi mi tu o zasoby rzadządzalne, które już znaczna część programistów wie, że należy zwalniać (wołając Dispose, bądź używając klauzuli using), ale o te zarządzalne. Jak to zatem możliwe, możesz spytać?

Garbage Collector to bardzo użyteczne “stworzenie”, ale to my jesteśmy “Twórcą” i GC może nam tylko służyć. Nie ma (jeszcze) wbudowanej sztucznej inteligencji, tak więc nie może domyślić się “co autor miał na myśli”. Rozpatrzmy sobie takie oto prosty program.

public Form1()

{

    int[] mem = new int[100000];

    for (int i = 0; i < 100000; i++)

    {

        mem[i] = new Random().Next();

    }

    InitializeComponent();

}

private Form1 childForm;

private void button1_Click(object sender, EventArgs e)

{

    childForm = new Form1();           

    childForm.ShowDialog();

    childForm = null;

}   

 

private void button2_Click(object sender, EventArgs e)

{

    GC.Collect();

}

Proste okno z dwoma przyciskami. Pierwszy z nich otwiera okno potomne. Drugi dla uwidocznienia działania wywołuje GC.Collect().

Zobaczmy jak będzie wyglądała pamięć naszego programu po 5-cio krotnym kliknięciu przycisku button1 a następnie oczyszczeniu pamięci przez GC (kliknij obrazek, aby zobaczyć powiększenie).

Widzimy dokładnie to czego się spodziewaliśmy. 5 instancji formy zostało zniszczonych. Zatem wszystko w porządku, tak? Nie do końca. Dodajmy coś co w aplikacjach .NET pojawia się nader często. Eventy.

Dodamy sobie do naszego prostego projektu klasę ustawień. Zrobimy z niej Singleton’a i dodamy możliwość podpięcia się pod zdarzenia, gdy nastąpi zmiana tych właściwości, tak aby formy mogły na nie zareagować.

public class Preferences

{

    private Preferences() {}

 

    private static Preferences instance;

    public static Preferences Instance

    {

        get

        {

            if (instance == null)

                instance = new Preferences();

            return instance;

        }

    }

 

    public void Notify()

    {

        var propertyChnaged = ProprtyChanged;

        if (propertyChnaged == null) return;

        propertyChnaged(this, EventArgs.Empty);

    }

 

    public event EventHandler ProprtyChanged;

}

A w konstruktorze formy dodajmy następującą linię:

Preferences.Instance.ProprtyChanged += Instance_ProprtyChanged;

Uruchommy naszą aplikację pod profiler’em i sprawdźmy jak się zachowa (kliknij obrazek, aby zobaczyć powiększenie).

Widzimy, że nasze formy przetrwały GC. Jak to możliwe? Dzieje się tak dlatego, że obiekt Preferences istnieje cały czas w aplikacji. I dobrze takie jego zadanie, jednak poprzez podpięcie się do niego eventem blokujemy przed GC jego usunięcie.

Rozwiązanie jest proste. Odpięcie tego eventu, gdy zamykamy formę. Proste, a często zapominane przez programistów. Na pewno widzicie pełno kodu, gdzie występuje tylko += a nie ma żadnego -=. Czasami może nam się upiec, nie zawsze występują takie warunki, że forma będzie blokowana przed GC. Jednak zawsze powinniśmy eventy odpisać. To powinien być taki nasz dobry nawyk.

Na zakończenie, zrzut z profiler’a z dodanym odpięciem zdarzenia (kliknij obrazek, aby zobaczyć powiększenie).

Wszystko znów w normie.

Znacie może jakieś inne fałszywe “prawdy” o .NET?