Najlepsze praktyki ułatwiające zbieranie elementów bezużytecznych w .Net

W Microsoft.Net wyrzucanie elementów bezużytecznych jest mechanizmem przyjętym przez środowisko wykonawcze języka wspólnego (CLR) do czyszczenia zasobów używanych przez aplikację. Obiekty tworzone w .Net są przechowywane w zarządzanym stercie. Chociaż musisz tworzyć obiekty, w większości przypadków nie musisz się martwić o ich czyszczenie - środowisko wykonawcze zrobi to za Ciebie.

Należy jednak zastosować najlepsze rozwiązania w aplikacji, aby ułatwić wyrzucanie elementów bezużytecznych i pomóc w szybszym czyszczeniu zasobów. Chociaż .Net jest biegły w odzyskiwaniu zarządzanych obiektów, należy przestrzegać pewnych wytycznych, aby przyspieszyć usuwanie elementów bezużytecznych i poprawić wydajność aplikacji. W tym artykule chciałbym przedstawić dyskusję o tym, jak działa odśmiecanie pamięci oraz o najlepszych praktykach związanych z ułatwieniem zbierania śmieci w .Net.

Kiedy odbywa się odśmiecanie?

Wyrzucanie elementów bezużytecznych ma miejsce, gdy w systemie jest mało dostępnej pamięci fizycznej lub GC.Collect()metoda jest wywoływana jawnie w kodzie aplikacji. Obiekty, które nie są już używane lub są nieosiągalne z katalogu głównego, są kandydatami do czyszczenia pamięci. Zasadniczo moduł odśmiecania czyści pamięć zajmowaną przez obiekty, które nie mają odniesień.

Pokolenia

Środowisko wykonawcze organizuje zarządzaną stertę na pokolenia. Wykorzystuje te pokolenia do organizowania krótko- i długowiecznych obiektów. Należy zaznaczyć, że w niższych pokoleniach znacznie częściej działa śmieciarka niż w wyższych. Generacja 0 zawiera krótkotrwałe obiekty, takie jak obiekty tymczasowe. Utworzony obiekt jest przechowywany w generacji 0, chyba że jest to duży obiekt. Jeśli obiekt jest dużym obiektem, jest on przechowywany w sterty dużych obiektów (LOH) w generacji 2. W większości przypadków obiekty generacji 0 są odzyskiwane przez moduł odśmiecania pamięci, gdy działa w tle.

Pisząc kod, należy przestrzegać pewnych sprawdzonych metod. Na przykład, powinieneś tworzyć obiekty w zakresie lokalnym tak bardzo, jak to tylko możliwe, aby ułatwić czyszczenie pamięci. Obiekty utworzone w wyższym zakresie zwykle znajdują się w pamięci przez dłuższy czas. Możesz skorzystać z profilera CLR, aby zrozumieć wzorce alokacji aplikacji.

Należy unikać wywoływania GC.Collect()metody, ponieważ powoduje to pełną kolekcję wszystkich generacji (generacja 0, 1 i 2). Po wywołaniu GC.Collect()metody środowisko uruchomieniowe odwiedza wszystkie aktywne obiekty w aplikacji. Zajmuje to dużo czasu i dlatego jest bardzo kosztowną operacją. W rezultacie wywoływanie GC.Collect()metody nie jest dobrą praktyką .

Jeśli musisz wywołać GC.Collect()metodę, powinieneś wywołać GC.WaitForPendingFinalizers()po wywołaniu, aby GC.Collect()upewnić się, że bieżący wątek wykonawczy czeka, aż finalizatory dla wszystkich obiektów zostaną wykonane.

Następnie powinieneś GC.Collect()ponownie wywołać metodę, aby upewnić się, że zebrałeś martwe obiekty, które pozostały. Te martwe obiekty, które mogły zostać utworzone w wyniku wywołania metody finalizatora na obiektach. Poniższy fragment kodu pokazuje, jak są używane te metody.

System.GC.Collect();

System.GC.WaitForPendingFinalizers();

System.GC.Collect();

Powinieneś upewnić się, że zminimalizowałeś ukryte alokacje i napisałeś swój kod w taki sposób, aby wyeliminować szanse na promocję krótkotrwałych obiektów do wyższych generacji. Nie powinieneś odnosić się do obiektów krótkotrwałych od tych długowiecznych, aby uniknąć promowania obiektów krótkotrwałych do wyższych pokoleń.

Powinieneś także unikać pisania finalizatorów dla swoich zajęć. Jeśli masz finalizator zaimplementowany w swojej klasie, obiekty takich klas stałyby się obiektami o długiej żywotności, ponieważ środowisko wykonawcze musi promować obiekty, które można sfinalizować, do starszych generacji. Należy ustawić obiekty na null przed wykonaniem długotrwałego wywołania, jeśli takie obiekty nie są potrzebne aplikacji. Jeśli nie potrzebujesz już statycznego obiektu lub innych obiektów w swojej aplikacji, powinieneś ustawić go na null przed wykonaniem długotrwałego wywołania. Nie należy ustawiać zmiennych lokalnych na null, ponieważ nie jest to potrzebne; środowisko wykonawcze może określić, do którego obiektu lokalnego nie ma odniesienia w kodzie lub nie jest on dalej używany, więc nie trzeba jawnie ustawiać żadnej zmiennej lokalnej na wartość null.