JavaRush /Blog Java /Random-PL /Przerwa kawowa #210. Wszystkie typy zbieraczy śmieci w Ja...

Przerwa kawowa #210. Wszystkie typy zbieraczy śmieci w Javie, o których powinieneś wiedzieć

Opublikowano w grupie Random-PL
Źródło: Hackernoon W tym poście poznasz mocne i słabe strony każdego typu modułu zbierającego elementy bezużyteczne używanego w programowaniu w języku Java. Przerwa kawowa #210.  Wszystkie typy zbieraczy śmieci w Javie, o których powinieneś wiedzieć - 1Pytanie o Garbage Collector (GC) można usłyszeć niemal w każdym wywiadzie. Postanowiłam więc zebrać o nich wszystkie niezbędne informacje, stosując moją ulubioną zasadę – krótko i prosto. Najpierw zacznijmy od celu CG i tego, dlaczego potrzebujemy wielu typów modułów zbierających elementy bezużyteczne. W językach takich jak C musimy przechowywać informacje o obiektach w pamięci i pisać dużo szablonowego kodu, aby zwolnić tę pamięć. Oczywiście wycieki pamięci są powszechne w takich programach. Java rozwiązuje problem wycieków pamięci za pomocą modułu zbierającego elementy bezużyteczne. A ty, jako programista, powinieneś wiedzieć, jakiego modułu zbierającego elementy bezużyteczne najlepiej użyć. Wiele zależy od tego, gdzie i jak działa Twój program. Może działać na słabym sprzęcie lub z dużą liczbą obiektów, albo Twój program musi być bardzo szybki. W oparciu o te warunki należy dostroić moduł zbierający elementy bezużyteczne, aby osiągnąć pożądaną wydajność. Zacznijmy więc.

Jak JVM radzi sobie z pamięcią

Wirtualna maszyna Java (JVM) dzieli pamięć na dwa obszary: stertę, w której przechowywane są dane aplikacji, oraz część niebędącą stertą, w której przechowywany jest kod programu i inne dane. Skupmy się na obszarze sterty. W tym miejscu nasz program tworzy nowe obiekty. Wszystkie moduły zbierające śmieci opierają się na tym, że wiele programów korzysta z obiektów efemerycznych. Oznacza to, że obiekty te zostały stworzone, następnie spełniły swoją funkcję i nie są już potrzebne. Większość takich obiektów. Ale niektóre obiekty żyją znacznie dłużej, być może nawet przez cały czas trwania programu. To tu pojawia się pomysł podziału obiektów na młodsze i starsze pokolenia. Musimy bardzo często sprawdzać, co dzieje się z młodszym pokoleniem. Faktem jest, że procesy zbierania śmieci dzielą się na drobne sprzątanie, które dotyczy tylko młodszego pokolenia, oraz sprzątanie całkowite, które może dotyczyć obu pokoleń. Pamiętaj, że moduł zbierający elementy bezużyteczne jest programem. Do pracy wymaga czasu i zasobów komputera. Co wpływa również na naszą aplikację. Jak to wpływa? Na przykład, aby wykonać zbieranie śmieci, JVM wstrzymuje naszą aplikację. Nazywa się to pauzą Stop-The-World (STW). W tym czasie wszystkie wątki aplikacji są zawieszone. Ale aplikacja znajdująca się w środku jest tego zupełnie nieświadoma. Dla aplikacji czas płynie równomiernie. Dlaczego jest tak źle? Wyobraź sobie, że piszesz jakiś wniosek o wymianę lub aplikację na autopilota samolotowego. Twoja aplikacja może przejść w tryb uśpienia na jedną sekundę, a charakter problemu może się radykalnie zmienić. Oznacza to, że pauza jest istotnym parametrem dla każdego modułu zbierającego elementy bezużyteczne. Następną podstawową właściwością modułu zbierającego śmieci jest całkowity czas spędzony na zbieraniu śmieci w stosunku do całkowitego czasu wykonania programu. Co to oznacza i dlaczego jest to tak ważne? Zamiast jednej dużej fazy „Zatrzymaj Świat” możemy wybrać algorytm z wieloma małymi przerwami. Preferowane są małe przerwy, ale nic nie jest za darmo. W tym przypadku płacimy zwiększając całkowity czas wykonania programu. I to też musimy wziąć pod uwagę. Kolejnym parametrem jest ilość zasobów sprzętowych. Każdy moduł zbierający potrzebuje pamięci do przechowywania informacji o obiekcie oraz procesora do czyszczenia. Ostatnim parametrem jest prędkość. Wydajność usuwania elementów bezużytecznych odnosi się do tego, jak szybko i efektywnie moduł zbierający elementy bezużyteczne (GC) odzyskuje pamięć, która nie jest już używana przez program. Wszystkie te parametry wpływają na algorytm, który może zwolnić pamięć tak szybko, jak to możliwe, zużywając minimalne zasoby. Przyjrzyjmy się dostępnym dla nas śmieciarzom. Do rozmowy kwalifikacyjnej musisz znać pierwsze pięć. Pozostałe dwa są dużo trudniejsze.

Seryjny GC

Serial GC to moduł zbierający elementy bezużyteczne wirtualnej maszyny Java, używany od początków Java. Jest to przydatne w przypadku programów z małą stertą i działających na słabszych komputerach. Ten moduł zbierający śmieci dzieli stertę na regiony, które obejmują Eden i Survivor. Region Eden to pula, z której początkowo przydzielana jest pamięć dla większości obiektów. Survivor to basen zawierający obiekty, które przetrwały wywóz śmieci w regionie Eden. W miarę zapełniania się sterty obiekty są przenoszone pomiędzy regionami Eden i Survivor. JVM stale monitoruje ruch obiektów do regionów Survivor i wybiera odpowiedni próg dla liczby takich ruchów, po czym obiekty są przenoszone do regionu Tenured. Gdy w regionie Tenured nie ma wystarczającej ilości miejsca, przejmuje pełne odśmiecanie, pracując na obiektach obu generacji. Główną zaletą tego modułu zbierającego elementy bezużyteczne są jego niskie wymagania dotyczące zasobów, więc do wykonania zbierania wystarczy procesor o niskim poborze mocy. Główną wadą Serial GC są długie przerwy podczas zbierania śmieci, szczególnie jeśli chodzi o duże ilości danych.

Równoległe CG

Równoległy moduł zbierający elementy bezużyteczne (Parallel CG) jest podobny do konstruktora sekwencyjnego. Obejmuje przetwarzanie równoległe niektórych zadań i możliwość automatycznego dostrajania ustawień wydajności. Parallel GC to moduł zbierający elementy bezużyteczne maszyny wirtualnej Java oparty na ideach Serial GC, ale z dodatkową równoległością i inteligencją. Jeśli komputer ma więcej niż jeden rdzeń procesora, starsza wersja JVM automatycznie wybiera Parallel GC. Sterta jest tutaj podzielona na te same regiony, co w Serial GC - Eden, Survivor 0, Survivor 1 i Old Gen (Tenured). Jednak wiele wątków uczestniczy równolegle w wyrzucaniu elementów bezużytecznych, a moduł zbierający może dostosować się do wymaganych parametrów wydajności. Każdy wątek zbierający ma obszar pamięci, który należy wyczyścić. Parallel GC ma również ustawienia mające na celu osiągnięcie wymaganej wydajności usuwania śmieci. Moduł zbierający używa statystyk z poprzednich wyrzucania elementów bezużytecznych do dostrajania ustawień wydajności dla przyszłych kolekcji. Parallel GC zapewnia automatyczne dostrajanie parametrów wydajności i krótsze czasy przerw w kompilacji, ale ma jedną drobną wadę w postaci fragmentacji pamięci. Nadaje się do większości zastosowań, jednak w przypadku bardziej złożonych programów lepiej wybrać bardziej zaawansowane implementacje modułu zbierającego elementy bezużyteczne. Plusy: W wielu przypadkach szybszy niż Serial GC. Ma dobrą prędkość. Wady: Zużywa więcej zasobów, a przerwy mogą być dość długie, ale możemy dostosować maksymalny czas trwania pauzy Stop-The-World.

Równoczesne przeszukiwanie Marka

Moduł zbierający elementy bezużyteczne Concurrent Mark Sweep (CMS) ma na celu zmniejszenie maksymalnej długości pauzy poprzez uruchomienie niektórych zadań usuwania elementów bezużytecznych jednocześnie z wątkami aplikacji. Ten moduł zbierający elementy bezużyteczne nadaje się do zarządzania dużymi ilościami danych w pamięci. Concurrent Mark Sweep (CMS) jest alternatywą dla Parallel GC w wirtualnej maszynie Java (JVM). Jest przeznaczony do aplikacji wymagających dostępu do wielu rdzeni procesora i wrażliwych na przerwy w trybie Stop-The-World. CMS wykonuje kroki usuwania śmieci równolegle z programem głównym, co pozwala na jego działanie bez zatrzymywania. Wykorzystuje tę samą organizację pamięci, co kolektory szeregowe i równoległe, ale nie czeka na zapełnienie obszaru dzierżawionego przed uruchomieniem czyszczenia starej generacji. Zamiast tego działa w tle i stara się zachować zwartość regionu Tenured. Concurrent Mark Sweep rozpoczyna się od początkowej fazy zaznaczania, która na krótko zatrzymuje główne wątki aplikacji i zaznacza wszystkie obiekty dostępne z poziomu roota. Następnie główne wątki aplikacji zostaną wznowione, a CMS rozpocznie wyszukiwanie wszystkich aktywnych obiektów, do których można uzyskać dostęp poprzez łącza z zaznaczonych obiektów głównych. Po zaznaczeniu wszystkich obiektów żywych kolekcjoner czyści pamięć obiektów martwych w kilku równoległych wątkach. Jedną z zalet systemu CMS jest skupienie się na minimalizacji przestojów, co ma kluczowe znaczenie w przypadku wielu aplikacji. Wymaga to jednak poświęceń w zakresie zasobów procesora i ogólnej przepustowości. Ponadto CMS nie kompresuje obiektów starej generacji, co prowadzi do fragmentacji. Długie przerwy spowodowane możliwymi awariami trybu równoległego mogą być niemiłą niespodzianką (choć nie zdarzają się często). Jeśli jest wystarczająca ilość pamięci, CMS może uniknąć takich przerw. Plusy: Szybki. Zawiera krótkie przerwy typu „Zatrzymaj świat”. Wady: zużywa więcej pamięci; jeśli nie ma wystarczającej ilości pamięci, niektóre pauzy mogą być długie. Niezbyt dobrze, jeśli aplikacja tworzy wiele obiektów.

Najpierw śmieci

Garbage-First (G1) jest uważany za alternatywę dla CMS-a, szczególnie w przypadku aplikacji serwerowych działających na serwerach wieloprocesorowych i zarządzających dużymi zbiorami danych. Moduł zbierający elementy bezużyteczne G1 konwertuje pamięć na wiele regionów o jednakowej wielkości, z wyjątkiem dużych regionów (które powstają w wyniku połączenia regularnych regionów w celu pomieszczenia ogromnych obiektów). Regiony nie muszą być zorganizowane szeregowo i mogą zmieniać przynależność pokoleniową. Dla młodszego pokolenia okresowo przeprowadzane są małe czystki i przenoszenie obiektów do regionów Ocalałych lub ulepszanie ich do starszej generacji i przekazywanie do Tenured. Czyszczenie odbywa się tylko w tych regionach, w których konieczne jest uniknięcie przekroczenia pożądanego czasu. Kolekcjoner sam przewiduje i wybiera do sprzątania regiony z największą ilością śmieci. Pełne przeciągnięcia wykorzystują pętlę zaznaczania do utworzenia listy aktywnych obiektów, która działa równolegle z główną aplikacją. Po cyklu znakowania G1 przełącza się na przeprowadzanie oczyszczania mieszanego, które dodaje regiony starszej generacji do zestawu regionów młodszej generacji, które mają zostać oczyszczone. Uważa się, że moduł zbierający elementy bezużyteczne G1 jest dokładniejszy niż moduł zbierający CMS w przewidywaniu rozmiarów przerw i lepiej rozkłada usuwanie elementów bezużytecznych w czasie, aby zapobiec długim przestojom aplikacji, szczególnie w przypadku dużych rozmiarów sterty. Nie fragmentuje również pamięci jak moduł zbierający CMS. Jednak kolektor G1 wymaga większej ilości zasobów procesora, aby działać równolegle z programem głównym, co zmniejsza przepustowość aplikacji. Plusy: Działa lepiej niż CMS. Ma krótsze przerwy. Wady: Zużywa więcej zasobów procesora. Zużywa również więcej pamięci, jeśli mamy wiele dość dużych obiektów (ponad 500 KB), ponieważ umieszcza takie obiekty w jednym regionie (1-32 MB).

Epsilon GC

Epsilon GC przeznaczony jest do sytuacji, w których nie jest wymagane zbieranie śmieci. Nie wykonuje wyrzucania elementów bezużytecznych, ale używa TLAB (lokalnych buforów alokacji wątków) do alokacji nowych obiektów - małych buforów pamięci żądanych przez poszczególne wątki ze sterty. Ogromne obiekty, które nie mieszczą się w buforze, żądają bloków pamięci specjalnie dla siebie. Gdy w Epsilon GC skończą się zasoby, generowany jest błąd OutOfMemoryError i proces zostaje zakończony. Do zalet Epsilon GC należą mniejsze wymagania dotyczące zasobów i szybsza alokacja pamięci dla aplikacji, które tworzą wszystkie obiekty potrzebne przy uruchomieniu lub uruchamiają krótkotrwałe aplikacje, które nie wykorzystują całej przydzielonej pamięci. Epsilon GC może również pomóc w analizie wymagań dotyczących zasobów, które inne moduły zbierające elementy bezużyteczne dodają do Twojej aplikacji. Plusy: Bardzo szybko. Wady: Nie czyści obiektów :) Kolejne dwa kolektory są najbardziej zaawansowanymi w swoim rodzaju, ale także najbardziej złożonymi. Dlatego rozważymy je pokrótce.

ZGC

ZGC może utrzymać opóźnienie poniżej milisekundy nawet w przypadku ogromnych ilości danych. ZGC to moduł zbierający elementy bezużyteczne opracowany przez firmę Oracle dla języka Java, zaprojektowany w celu zapewnienia dużej przepustowości i małych opóźnień podczas przetwarzania dużych stert (do 16 TB). ZGC opiera się na zasadach pamięci wirtualnej i wykorzystuje różne oznaczenia kolorystyczne do śledzenia stanu obiektów podczas usuwania śmieci. Plusy: Przerwy trwają krócej niż milisekundę, nawet w przypadku dużych stert, co jest bardzo przydatne w aplikacjach wymagających krótkiego czasu przetwarzania zapytań. Działa z bardzo dużymi stertami z dobrą przepustowością. ZGC może kompresować pamięć sterty podczas usuwania śmieci. Wady: wysokie użycie procesora i znaczne wymagania dotyczące wydajności, które mogą spowolnić czas uruchamiania aplikacji.

Shenandoah G.C.

Shenandoah GC to kolejny moduł zbierający elementy bezużyteczne z krótkimi przerwami, niezależnie od wielkości sterty. Ten moduł zbierający elementy bezużyteczne został opracowany przez firmę Red Hat. Został zaprojektowany tak, aby zminimalizować czas spędzany przez aplikację na zbieraniu śmieci. Podobnie jak ZGC jest to kolektor równoległy, co oznacza, że ​​działa podczas działania aplikacji, minimalizując przerwy. Shenandoah GC używa „wskaźników przekazywania” do przenoszenia obiektów podczas zbierania elementów bezużytecznych. Posiada również technikę zwaną „usuwaniem bariery ładunkowej”, aby poprawić wydajność. Plusy: Shenandoah GC może osiągnąć krótkie czasy pauzy, często mniejsze niż 10 ms, nawet w przypadku ogromnych stosów. Dobra przepustowość. Wady: duże obciążenie procesora i trudność w pracy pod dużym obciążeniem.

Wniosek

Odbieracze śmieci to jedno z najtrudniejszych zadań w programowaniu. Stale wprowadzane są nowe rozwiązania w tym kierunku. Chociaż programiści rzadko modyfikują GC, nadal musisz mieć przynajmniej podstawową wiedzę na temat działania narzędzia do usuwania śmieci.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION