Garbage Collection (GC): co to jest, algorytmy i zbieranie śmieci w tworzeniu aplikacji mobilnych

Autor: IT Sectr Opublikowano: 2026-03-29 Czas czytania: 10 min

Automatyczne zarządzanie pamięcią poprzez zbieranie śmieci to kluczowy mechanizm platformy Android, oparty na maszynie wirtualnej ART. Według Google Android Documentation, 2026, garbage collector uwalnia programistę od ręcznego zarządzania pamięcią, automatycznie usuwając obiekty, do których nie ma już referencji. Bez GC każde przydzielenie obiektu wymagałoby jawnego wywołania free lub delete, co w ekosystemie Java z milionami obiektów na sekundę jest fizycznie niemożliwe.

Najważniejsze

  • Garbage Collection — automatyczny mechanizm zwalniania pamięci poprzez usuwanie nieużywanych obiektów w Javie i Androidzie
  • Podstawowe algorytmy — Mark-and-Sweep, Copying Collection i Generational Collection określają efektywność zbierania
  • ART i Dalvik — dwie implementacje maszyny wirtualnej Android, gdzie ART (Android Runtime) zastąpił Dalvik począwszy od Android 5.0
  • GC Pauses — zatrzymania wykonania aplikacji podczas zbierania — główna przyczyna jank i problemów z wydajnością
  • Optymalizacja GC — zmniejszenie alokacji, wykorzystanie pul obiektów i właściwy dobór typów Collection zmniejszają obciążenie garbage collectora

Czym jest Garbage Collection (GC)?

Garbage Collection (GC) — to automatyczny proces wykrywania i zwalniania pamięci zajętej przez obiekty, które nie są już używane przez program. W kontekście tworzenia aplikacji mobilnych GC jest stosowany na platformie Android poprzez maszynę wirtualną ART, a także w standardowej Java Virtual Machine.

W przeciwieństwie do języków z ręcznym zarządzaniem pamięcią (C, C++), gdzie programista musi jawnie wywoływać free lub delete, GC całkowicie przejmuje zadanie śledzenia cyklu życia obiektów. Programista tworzy nowe obiekty przez operator new, a garbage collector określa moment, w którym obiekt staje się nieosiągalny — to znaczy, że nie pozostała do niego żadna aktywna referencja.

Główna metryka efektywności GC — czas pauzy (pause time) i przepustowość (throughput). Pauza to odcinek, na który wstrzymywane jest wykonywanie aplikacji w celu przeprowadzenia zbierania. W środowisku mobilnym pauzy dłuższe niż 8–16 milisekund są zauważalne jako pominięte klatki (jank).

Według danych Google I/O 2019, ART w Android 10 skrócił typowe pauzy GC do 2–4 ms, co jest o 70% mniej w porównaniu z Dalvik w Android 4.4. Niemniej jednak, nieprawidłowa praca z pamięcią — częsta alokacja obiektów w pętlach, tworzenie tymczasowych instancji bez potrzeby — pozostaje główną przyczyną problemów z wydajnością.

Jak działa garbage collector: podstawowe algorytmy

Wszystkie implementacje GC w Javie i Androidzie opierają się na kilku fundamentalnych algorytmach, które są łączone w celu osiągnięcia równowagi między czasem pauzy a kompletnością czyszczenia. Zrozumienie tych algorytmów jest niezbędne do pisania GC-friendly kodu.

Mark-and-Sweep

Mark-and-Sweep — najprostszy algorytm działający w dwóch etapach. W fazie Mark garbage collector przechodzi przez graf obiektów, zaczynając od korzeni (root set) — zmiennych lokalnych, pól statycznych, stosów wątków. Każdy osiągalny obiekt jest oznaczany flagą live. W fazie Sweep garbage collector przechodzi przez całą stertę i zwalnia pamięć nieoznaczonych obiektów.

Wadą jest fragmentacja pamięci: po Sweep wolne obszary przeplatają się z zajętymi, co utrudnia przydzielanie dużych obiektów. W scenariuszach mobilnych jest to krytyczne, ponieważ sterta jest zwykle mała (64–512 MB na Androidzie).

Copying Collection

Copying Collection dzieli stertę na dwie półprzestrzenie (semi-spaces). Aktywne obiekty są kopiowane z jednej półprzestrzeni do drugiej w sposób zwarty, bez przerw. Po skopiowaniu stara półprzestrzeń jest całkowicie uznawana za wolną. Algorytm całkowicie eliminuje fragmentację, ale wymaga dwa razy więcej pamięci.

W środowiskach mobilnych Copying Collection jest stosowany przez garbage collectory generacyjne do szybkiego czyszczenia młodych obiektów, które statystycznie umierają wcześnie (hipoteza słabego pokolenia).

Generational Collection

Generational Collection dzieli stertę na pokolenia: Young Generation (młode obiekty) i Old Generation (stare, które przetrwały kilka kolekcji). Zbieranie młodego pokolenia (Minor GC) jest wykonywane często i szybko, ponieważ większość obiektów umiera młodymi. Zbieranie starego pokolenia (Major GC lub Full GC) zdarza się rzadziej, ale trwa dłużej.

java
// Demonstracja generacyjnego GC: młode obiekty umierają szybko
void processItems(List<Item> items) {
    List<Result> results = new ArrayList<>();       // żyje przez całą metodę
    for (Item item : items) {
        Result r = new Result(item.getValue());    // umiera natychmiast
        if (r.isValid()) {
            process(r);                               // r staje się śmieciem
        }
    }
    saveResults(results);                             // results przechodzi do Old Gen
}

W tym przykładzie obiekty Result są tworzone wewnątrz pętli i natychmiast stają się śmieciem — są idealnymi kandydatami dla Young GC. Obiekt results żyje dłużej i migruje do Old Generation. Podział na pokolenia pozwala Minor GC czyścić młode obiekty w milisekundach, nie dotykając starej sterty.

Garbage Collection w Androidzie: ART i Dalvik

Android przeszedł drogę od Dalvik VM do ART (Android Runtime), a implementacja GC jest jedną z kluczowych różnic między nimi. Zrozumienie architektury GC w Androidzie pomaga pisać kod, który minimalizuje pauzy na rzeczywistych urządzeniach.

CechaDalvik (do 4.4)ART (5.0+)
Typ GCMark-and-Sweep z Concurrent MarkGeneracyjny + Concurrent
Typowa pauza10–30 ms2–4 ms
KompaktyzacjaNie (tylko fragmentacja rośnie)Tak (w tle, bez zatrzymywania aplikacji)
Kompilacja AOTJIT (Just-In-Time)AOT + JIT (hybrydowa)

Dalvik GC

Dalvik używał kombinacji Mark-and-Sweep z fazą concurrent. Concurrent Mark pozwalał aplikacji kontynuować pracę podczas przechodzenia grafu obiektów, ale faza Sweep wymagała zatrzymania wszystkich wątków (Stop-The-World). Na urządzeniach z małą ilością RAM (512 MB — 1 GB) pauzy osiągały 30 ms, co powodowało zauważalne przycięcia interfejsu. Ponadto Dalvik nie kompaktyzował sterty, dlatego po dłuższej pracy rosła fragmentacja, a przydzielanie dużych obiektów (np. Bitmap) mogło wyrzucić OutOfMemoryError przy wystarczającej całkowitej ilości wolnej pamięci.

ART GC

ART (Android Runtime) wprowadził generacyjny garbage collector z kompaktyzacją współbieżną. Sterta jest dzielona na trzy regiony: Young, Mature (odpowiednik Old Generation) i Large Object Space (dla obiektów większych niż 12 KB). Zbieranie regionu Young odbywa się równolegle bez zatrzymywania wątków w większości przypadków. W Android 10+ pojawiła się Concurrent Copying — kompaktyzacja wykonywana w wątku tła bez Stop-The-World.

Dzięki architekturze ART, typowe pauzy GC zmniejszyły się do 2–4 ms, a w scenariuszach z przewagą młodych obiektów — do 0.5–1 ms. Pozwoliło to urządzeniom Android zapewniać stabilne 60 FPS nawet przy aktywnej pracy z pamięcią.

Rodzaje garbage collectorów w Javie

W ekosystemie Java istnieje kilka implementacji GC, każda z własnym profilem wydajności. Dla tworzenia aplikacji na Android wybór jest ograniczony do ART, ale znajomość Java GC jest przydatna przy pisaniu części serwerowej aplikacji mobilnych oraz przy tworzeniu aplikacji w Kotlin Multiplatform.

Serial GC

Serial GC — jednowątkowy garbage collector z całkowitym zatrzymaniem aplikacji (Stop-The-World). Każda operacja Mark, Sweep i Compact jest wykonywana przez jeden wątek. Wydajność jest niska — nie jest stosowany w serwerach mobilnych. Nadaje się tylko do małych aplikacji ze stertą do 100 MB.

Parallel GC

Parallel GC (znany również jako Throughput Collector) używa wielu wątków dla wszystkich faz zbierania. Jest zorientowany na maksymalną przepustowość (throughput) — minimalizuje czas poświęcony na GC względem czasu działania aplikacji. Włączany przez flagę -XX:+UseParallelGC w JVM.

G1 GC

G1 (Garbage-First) GC — domyślny garbage collector w Java 9+. Sterta jest dzielona na regiony po 1–32 MB. G1 przewiduje czas pauzy i dąży do zmieszczenia się w zadanym limicie (domyślnie 200 ms). Priorytet: najpierw czyszczone są regiony z największą ilością śmieci (stąd nazwa). G1 jest efektywny dla serwerów z dużą stertą (4–64 GB) i przewidywalnymi pauzami.

java
// Włączenie G1 GC z docelowym czasem pauzy 100 ms
// java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar app.jar

public class MemoryMonitor {
    private static final long THRESHOLD = 512 * 1024 * 1024; // 512 MB

    public void checkHeapUsage() {
        Runtime rt = Runtime.getRuntime();
        long used = rt.totalMemory() - rt.freeMemory();
        if (used > THRESHOLD) {
            System.out.println("Przekroczenie progu użycia sterty: " + used);
            System.out.println("Rozważ zmniejszenie alokacji");
        }
    }
}

Monitorowanie sterty przez Runtime pozwala wykrywać wycieki pamięci na wczesnym etapie. Jeśli used przekracza 80% maksymalnej sterty przy stabilnej pracy — to sygnał o możliwym wycieku lub nadmiernym zużyciu pamięci przez aplikację.

Problemy GC i optymalizacja pamięci w aplikacjach mobilnych

Nawet nowoczesny ART GC nie rozwiązuje wszystkich problemów — nieprawidłowe używanie pamięci pozostaje główną przyczyną jank i ANR (Application Not Responding). Przyjrzyjmy się głównym scenariuszom i metodom optymalizacji.

GC Pauses i Jank

GC Pauses — zatrzymania wątków aplikacji na czas zbierania. Na ekranie objawia się to jako pominięte klatki, gdy czas między dwiema klatkami przekracza 16.6 ms (60 FPS). Jeśli GC trwa 30 ms, rysowana jest tylko jedna klatka zamiast dwóch — użytkownik widzi przycięcia interfejsu.

Główne przyczyny długich pauz: duża liczba żywych obiektów w Old Generation, fragmentacja sterty, częste Full GC. Do diagnostyki używa się Android Studio Profiler i systrace.

Zmniejszanie obciążenia GC

Główna zasada GC-friendly kodu — minimalizować liczbę alokowanych obiektów. Każdy nowy obiekt wymaga nie tylko przydzielenia pamięci, ale także późniejszego zebrania. Nawet jeśli GC jest szybki, 1000 zbędnych alokacji na sekundę daje 1000 sprawdzeń dla garbage collectora.

  • Unikaj tworzenia obiektów w pętlach — wynieś tworzenie poza pętlę, używaj ponownie zmiennych lokalnych
  • Używaj pul obiektów — dla Bitmap, byte[] i innych ciężkich struktur stosuj Object Pool lub RecyclerView.ViewHolder
  • Preferuj typy prymitywne — int zamiast Integer, float zamiast Float unikają autoboxingu
  • Używaj SparseArray — zamiast HashMap<Integer, V> w Android SDK dostępny jest SparseArray, LongSparseArray, które działają z typami prymitywnymi
  • StringBuilder zamiast konkatenacji — każde dodanie stringów tworzy nowy obiekt String

Wycieki pamięci

Wyciek pamięci występuje, gdy obiekt pozostaje osiągalny, mimo że nie jest już potrzebny. GC nie może usunąć takiego obiektu, a pamięć stopniowo się wyczerpuje. Typowe przyczyny: nieodpięte listenery, statyczne referencje do Activity, anonimowe klasy przechwytujące zewnętrzny kontekst oraz niezamknięte Cursor/InputStream.

java
// Wyciek pamięci: anonimowa klasa przechowuje referencję do Activity
public void startTask() {
    new Thread(new Runnable() {                    // niejawnie przechowuje this (Activity)
        @Override
        public void run() {
            // długa operacja...
            System.out.println("Gotowe");
        }
    }).start();
}

// Poprawka: statyczna klasa zagnieżdżona + WeakReference
private static class TaskRunnable implements Runnable {
    private WeakReference<Activity> activityRef;

    TaskRunnable(Activity activity) {
        this.activityRef = new WeakReference<>(activity);
    }

    @Override
    public void run() {
        Activity act = activityRef.get();
        if (act != null) {
            // bezpieczna praca z Activity
        }
    }
}

W tym przykładzie anonimowy Runnable przechwytuje niejawną referencję do Activity. Dopóki wątek żyje — Activity nie może być zebrane przez GC, nawet jeśli użytkownik już zamknął ekran. Poprawka WeakReference + static class przerywa ten łańcuch i pozwala na utylizację Activity.

Często zadawane pytania

Czym różni się GC w Androidzie od GC w Javie?

GC w Androidzie (ART) to generacyjny garbage collector z kompaktyzacją współbieżną, zoptymalizowany dla urządzeń mobilnych z ograniczoną pamięcią. Java GC (G1, ZGC) to serwerowe garbage collectory z dużymi stertami i przewidywalnymi pauzami. ART GC nie używa flag JVM — cała konfiguracja jest wykonywana automatycznie na poziomie systemu operacyjnego.

Czym jest Stop-The-World w GC?

Stop-The-World — moment, w którym garbage collector wstrzymuje wszystkie wątki aplikacji, aby bezpiecznie przejść przez graf obiektów lub zwolnić pamięć. Im dłuższy STW, tym bardziej zauważalny jest jank. ART skrócił typowy czas STW do 2–4 ms dzięki architekturze generacyjnej.

Jak wykryć wyciek pamięci w Androidzie?

Użyj Android Studio Memory Profiler — pokazuje wzrost sterty, liczbę alokacji i umożliwia wykonanie Heap Dump. Do głębokiej analizy używaj LeakCanary — biblioteka automatycznie wykrywa wycieki i pokazuje łańcuch referencji uniemożliwiających zbieranie GC.

Kiedy występuje Full GC i dlaczego jest niebezpieczny?

Full GC — pełne zbieranie wszystkich pokoleń sterty, w tym Old Generation. W aplikacjach mobilnych Full GC może trwać 50–200 ms, powodując zauważalny jank lub ANR. Główne przyczyny: fragmentacja sterty, wycieki pamięci, przekroczenie progu Old Generation.

Jak Kotlin pomaga uniknąć wycieków pamięci?

Kotlin zapewnia korutyny ze strukturalną współbieżnością — anulowanie zakresu automatycznie anuluje wszystkie korutyny potomne, zapobiegając wyciekom. Ponadto w Kotlinie jest delegat lazy do leniwej inicjalizacji oraz operatory zakresu widzialności, które zmniejszają liczbę tymczasowych obiektów.

Podsumowanie

  • Garbage Collection — automatyczne zarządzanie pamięcią poprzez usuwanie nieosiągalnych obiektów, podstawa Android Runtime
  • Mark-and-Sweep — podstawowy algorytm z dwufazowym zbieraniem, cierpi na fragmentację sterty
  • Copying Collection — eliminuje fragmentację poprzez kopiowanie żywych obiektów do zwartej półprzestrzeni
  • Generational GC — dzieli stertę na pokolenia (Young/Old), przyspieszając zbieranie młodych krótkożyjnych obiektów
  • ART w Androidzie — generacyjny garbage collector z kompaktyzacją współbieżną i pauzami 2–4 ms, który zastąpił Dalvik w Android 5.0
  • Optymalizacja GC — zmniejszenie alokacji, pule obiektów, typy prymitywne zamiast opakowań i SparseArray zamiast HashMap zmniejszają obciążenie garbage collectora
  • Diagnostyka — Android Studio Profiler, systrace i LeakCanary to główne narzędzia do wykrywania problemów z pamięcią

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również