Garbage collection

Automatyczny proces zwalniania pamięci zajmowanej przez dane, których program już nie używa. Odciąża programistę od ręcznego zarządzania pamięcią.

Garbage collection (GC, po polsku odśmiecanie pamięci) to automatyczny mechanizm, który sam wyszukuje dane niepotrzebne już programowi i zwalnia zajmowaną przez nie pamięć. Zamiast ręcznie pilnować, kiedy zwolnić każdy kawałek pamięci, oddajesz tę robotę środowisku uruchomieniowemu (runtime). Korzystają z tego m.in. Java, C#, Go, Python, JavaScript czy Ruby.

Jak to działa

GC działa na prostym założeniu: jeśli do jakiegoś obiektu nie da się już dotrzeć z aktywnej części programu (zmiennych, stosu, pól statycznych), to znaczy, że nikt go nie używa i można go usunąć. Takie obiekty nazywamy nieosiągalnymi (unreachable). Klasyczny algorytm mark-and-sweep najpierw oznacza wszystko, co jest osiągalne, a potem zamiata resztę. Nowsze podejścia, jak generational GC, korzystają z obserwacji, że większość obiektów umiera młodo — więc dzielą pamięć na pokolenia i częściej sprzątają to świeże.

Dzięki temu nie musisz pamiętać o free() jak w C, a cała klasa błędów — wycieki pamięci po zapomnianym zwolnieniu, podwójne zwolnienie, dostęp do zwolnionej pamięci (use-after-free) — w dużej mierze znika. Cena? GC bywa nieprzewidywalne czasowo i potrafi na chwilę zatrzymać aplikację.

Przykład z praktyki

W JVM (Java) domyślnym garbage collectorem od Javy 9 jest G1GC. Możesz podejrzeć, co się dzieje z pamięcią, włączając logi GC przy starcie aplikacji:

java -Xlog:gc -jar mojaapka.jar

W logach zobaczysz pauzy typu „GC pause”, ich długość i ile pamięci odzyskano. Jeśli pauzy są za długie i psują responsywność, możesz przełączyć się na kolektor o niskich opóźnieniach, np. ZGC, dodając -XX:+UseZGC. W Pythonie z kolei masz hybrydę: liczenie referencji (reference counting) plus dodatkowy GC łapiący cykle — ręcznie odpalisz go przez gc.collect().

Częste mity i pułapki

Mit: „GC = brak wycieków pamięci”. Nieprawda. Jeśli trzymasz referencję do obiektu, którego już nie potrzebujesz (np. rosnąca w nieskończoność lista albo statyczna mapa cache), GC uzna go za potrzebny i nigdy nie zwolni. To wyciek logiczny — i potrafi przewrócić serwer.

Mit: „Wywołam GC ręcznie i będzie szybciej”. W Javie System.gc() to tylko sugestia, którą JVM zwykle ignoruje lub traktuje opieszale. Wymuszanie GC częściej szkodzi, niż pomaga. Uważaj też na pauzy „stop-the-world” w aplikacjach czasu rzeczywistego — tam czasem świadomie wybiera się języki bez GC.

Pojęcia powiązane: zarządzanie pamięcią, sterta (heap), stos (stack), wyciek pamięci, reference counting, mark-and-sweep, generational GC, RAII i ownership (model bez GC z Rusta/C++).