Programując w Java nie zwalniasz pamięci ręcznie. Nie wywołujesz free(), nie martwisz się o wskaźniki. A jednak aplikacja działa stabilnie, obiekty znikają, a pamięć się nie „zapcha”.
Za kulisami pracuje Garbage Collector (GC) – mechanizm automatycznego zarządzania pamięcią w ramach Java Virtual Machine (JVM).
Co to właściwie jest i jak działa. Postaram się po krótce wyjaśnić Ci to w poniższym wpisie.
Z perspektywy GC najważniejsze są dwa obszary:
przechowuje zmienne lokalne i wywołania metod
działa szybko i jest czyszczony automatycznie po zakończeniu metody
GC się nim nie zajmuje
tutaj trafiają wszystkie obiekty tworzone przez new
to właśnie ten obszar analizuje i czyści Garbage Collector
Obiekt nadaje się do usunięcia, gdy:
nie ma do niego żadnej referencji
nie jest osiągalny z tzw. GC Roots
GC rozpoczyna przeszukiwanie od:
aktywnych wątków
zmiennych lokalnych na stosie
statycznych pól klas
referencji JNI
Jeżeli od żadnego z tych punktów nie prowadzi ścieżka do obiektu – staje się on kandydatem do usunięcia.
Klasyczny model to:
GC przechodzi graf obiektów od GC Roots i oznacza wszystkie osiągalne.
Obiekty nieoznaczone są usuwane z pamięci.
Pamięć jest porządkowana, aby zmniejszyć fragmentację.
Nowoczesne collectory stosują bardziej zaawansowane warianty, ale zasada pozostaje podobna.
Empiryczna obserwacja:
Większość obiektów żyje bardzo krótko.
Dlatego heap podzielono na generacje:
Eden - każdy nowy obiekt trafia właśnie tutaj
Survivor 0
Survivor 1
Jak już wspominałem wcześniej każdy nowy obiekt trafia do Eden. Gdy Eden się zapełni uruchamiany jest Minor GC.
Minor GC działa według następującego schematu:
Każdorazowe przeżycie obiektu powoduje zwiększenie jego licznika (licznik oznacza ile razy obiekt przeżył Minor GC). Po przekroczeniu przez obiekt określonego progu trafia on do Old Generation.
Zapewnie zapytasz teraz do czego służa Survivor 0 oraz Survivor 1. Juz spieszę z wyjaśnieniami:
Obszary Survivor tworzą mechanizm rotacyjny stosowany po każdym Minor GC. Jeden jest źródłem, a drugi celem kopiowania. Przy następnym Minor GC role się odwracają. Ten proces zapobiega fragmentacji.
Kiedy obiekty trafiają do Old Generation:
Obiekty, które przetrwały kilka cykli Minor GC.
Czyszczenie tej przestrzeni to Major GC – rzadsze, ale bardziej kosztowne ze względu na potrzebę przeanalizowania wiekszej ilości danych, relacji między obiektami oraz referencji.
Od Java 8 przechowuje metadane klas (wcześniej PermGen).
Współczesna Java oferuje kilka implementacji GC.
jeden wątek
dobre rozwiązanie dla małych aplikacji
wielowątkowy
nastawiony na maksymalną przepustowość
dzieli heap na regiony
czyści najbardziej „opłacalne” obszary
domyślny w wielu wersjach Javy
bardzo krótkie pauzy (nawet przy dużych heapach)
odpowiedni dla systemów o niskiej tolerancji opóźnień
minimalizacja czasu „Stop The World”
równoległa kompaktacja
Tak, w niektórych fazach.
Podczas tzw. STW pause wszystkie wątki aplikacji są wstrzymywane.
Nowoczesne GC (np. G1 czy ZGC) redukują czas tych pauz do minimum.
Java udostępnia kilka typów referencji:
| Typ referencji | Kiedy usuwany obiekt? | Zastosowanie |
|---|---|---|
| Strong | Nigdy, dopóki istnieje referencja | Standardowe użycie |
| WeakReference | Przy kolejnym GC | Mapy cache |
| SoftReference | Przy braku pamięci | Cache obrazów |
| PhantomReference | Po finalizacji | Zaawansowane zarządzanie zasobami |
System.gc();
To tylko sugestia dla JVM.
Nie ma gwarancji natychmiastowego wykonania.
W aplikacjach produkcyjnych lepiej unikać takiego podejścia.
static List<Object> cache = new ArrayList<>();
Tu GC nie pomoże – należy używać try-with-resources.
Brak wyrejestrowania powoduje utrzymywanie referencji.
Może prowadzić do częstych Minor GC i spadku wydajności.
Przykładowe flagi:
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xms512m -Xmx2g
Dla aplikacji opartych o Spring Framework odpowiednia konfiguracja GC może znacząco poprawić stabilność przy dużym obciążeniu.
Garbage Collector w Javie to:
✔ analiza grafu obiektów
✔ generacyjny model zarządzania pamięcią
✔ kompromis między wydajnością a czasem pauzy
✔ mechanizm, który znacząco zwiększa bezpieczeństwo aplikacji
Zrozumienie działania GC to moment, w którym przestajesz być „użytkownikiem Javy”, a zaczynasz świadomie pracować z JVM. Zrozumienie generacyjnego modelu pozwala:
✔ pisać kody przyjazny GC
✔ unikać dużych alokacji w pętlach
✔ projektować cache swiadomie
✔ sprawnie diagnozować problemy wydajnościowe
W ramach powtórki poniżej przedstawiam wam listę kolekcji i interfejsów oraz wyjaśnię jak działa proces iteracji po elementach Set i Map, które nia maja określonego porzadku …
Na pierwszy rzut oka obiekt w Javie wydaje się czymś prostym: tworzysz go, używasz i… zapominasz. W praktyce jednak każdy obiekt przechodzi kilka wyraźnych etapów, a ich zrozumienie pomogło mi …
W Javie obiekty nie pojawiają się znikąd. Zanim zaczną „żyć” w pamięci programu, muszą zostać poprawnie zainicjalizowane. Właśnie w tym momencie do gry wchodzą konstruktory – specjalne metody odpowiedzialne …
Porównywanie napisów w Javie to jeden z tych tematów, które na pierwszy rzut oka wydają się banalne. W praktyce jednak bardzo często prowadzą do nieoczekiwanych wyników i trudnych do znalezienia …
Komentarze (0)
Nie dodano jeszcze żadnych komentarzy
Dodaj nowy komentarz: