Garbage Collection (GC): co to je, algoritmy a sběr odpadků v mobilním vývoji

Autor: IT Sectr Publikováno: 2026-03-29 Doba čtení: 10 min

Automatické řízení paměti prostřednictvím sběru odpadu je klíčovým mechanismem platformy Android, založeným na virtuálním stroji ART. Podle Google Android Documentation, 2026, sběrač odpadků osvobozuje vývojáře od ručního řízení paměti, automaticky odstraňuje objekty, na které již neexistují žádné reference. Bez GC by každé přidělení objektu vyžadovalo explicitní volání free nebo delete, což je v ekosystému Java s miliony objektů za sekundu fyzicky nemožné.

Hlavní body

  • Garbage Collection — automatický mechanismus uvolňování paměti odstraněním nepoužívaných objektů v Javě a Androidu
  • Základní algoritmy — Mark-and-Sweep, Copying Collection a Generational Collection určují účinnost sběru
  • ART a Dalvik — dvě implementace virtuálního stroje Android, kde ART (Android Runtime) nahradil Dalvik počínaje Androidem 5.0
  • GC Pauses — zastavení provádění aplikace během sběru — hlavní příčina jank a problémů s výkonem
  • Optimalizace GC — snížení alokací, použití objektových poolů a správný výběr typů Collection snižují zatížení sběrače

Co je Garbage Collection (GC)?

Garbage Collection (GC) — je automatický proces detekce a uvolňování paměti obsazené objekty, které již program nepoužívá. V kontextu mobilního vývoje je GC aplikován na platformě Android prostřednictvím virtuálního stroje ART, stejně jako ve standardním Java Virtual Machine.

Na rozdíl od jazyků s ručním řízením paměti (C, C++), kde programátor musí explicitně volat free nebo delete, GC plně přebírá úkol sledování životního cyklu objektů. Vývojář vytváří nové objekty pomocí operátoru new a sběrač určuje okamžik, kdy se objekt stává nedosažitelným — tedy na něj neexistuje žádná aktivní reference.

Hlavní metrikou účinnosti GC — doba pauzy (pause time) a průchodnost (throughput). Pauza je úsek, na který je pozastaveno provádění aplikace za úèelem provedení sběru. V mobilním prostředí jsou pauzy delší než 8–16 milisekund patrné jako vynechané snímky (jank).

Podle údajů Google I/O 2019, ART v Androidu 10 snížil typické pauzy GC na 2–4 ms, což je o 70% méně ve srovnání s Dalvikem v Androidu 4.4. Nicméně nesprávná práce s pamětí — časté přidělování objektů ve smyčkách, vytváření dočasných instancí bez nutnosti — zůstává hlavní příčinou problémů s výkonem.

Jak funguje sběrač odpadků: základní algoritmy

Všechny implementace GC v Javě a Androidu jsou založeny na několika základních algoritmech, které jsou kombinovány pro dosažení rovnováhy mezi dobou pauzy a úplností čištění. Pochopení těchto algoritmů je nezbytné pro psaní GC-friendly kódu.

Mark-and-Sweep

Mark-and-Sweep — nejjednodušší algoritmus pracující ve dvou fázích. Ve fázi Mark sběrač prochází graf objektů, počínaje kořenovými referencemi (root set) — lokálními proměnnými, statickými poli, zásobníky vláken. Každý dosažitelný objekt je označen příznakem live. Ve fázi Sweep sběrač prochází celý heap a uvolňuje paměť neoznačených objektů.

Nevýhodou je fragmentace paměti: po Sweepu se volné oblasti střídají s obsazenými, což ztěžuje přidělování velkých objektů. V mobilních scénářích je to kritické, protože heap je obvykle malý (64–512 MB na Androidu).

Copying Collection

Copying Collection rozděluje heap na dva poloprostory (semi-spaces). Aktivní objekty jsou kopírovány z jednoho poloprostoru do druhého kompaktně, bez mezer. Po zkopírování je starý poloprostor prohlášen za zcela volný. Algoritmus zcela eliminuje fragmentaci, ale vyžaduje dvakrát více paměti.

V mobilních prostředích je Copying Collection používán generačními sběrači pro rychlé čištění mladých objektů, které statisticky umírají brzy (hypotéza slabé generace).

Generational Collection

Generational Collection rozděluje heap na generace: Young Generation (mladé objekty) a Old Generation (staré, které přežily několik sběrů). S běr mladé generace (Minor GC) se provádí často a rychle, protože většina objektů umírá mladá. S běr staré generace (Major GC nebo Full GC) nastává méně často, ale trvá déle.

java
// Demonstrace generačního GC: mladé objekty umírají rychle
void processItems(List<Item> items) {
    List<Result> results = new ArrayList<>();       // žije celou metodu
    for (Item item : items) {
        Result r = new Result(item.getValue());    // umírá okamžitě
        if (r.isValid()) {
            process(r);                               // r se stává odpadem
        }
    }
    saveResults(results);                             // results přechází do Old Gen
}

V tomto příkladu jsou objekty Result vytvářeny uvnitř smyčky a okamžitě se stávají odpadem — jsou ideálními kandidáty pro Young GC. Objekt results žije déle a migruje do Old Generation. Oddělení generací umožňuje Minor GC čistit mladé objekty v milisekundách, aniž by se dotkl starého heapu.

Garbage Collection v Androidu: ART a Dalvik

Android prošel cestou od Dalvik VM k ART (Android Runtime) a implementace GC je jedním z klíčových rozdílů mezi nimi. Pochopení architektury GC v Androidu pomáhá psát kód, který minimalizuje pauzy na skutečných zařízeních.

CharakteristikaDalvik (do 4.4)ART (5.0+)
Typ GCMark-and-Sweep s Concurrent MarkGenerační + Concurrent
Typická pauza10–30 ms2–4 ms
KompaktaceNe (pouze fragmentace roste)Ano (na pozadí, bez zastavení aplikace)
AOT kompilaceJIT (Just-In-Time)AOT + JIT (hybridní)

Dalvik GC

Dalvik používal kombinaci Mark-and-Sweep s concurrent fází. Concurrent Mark umožňoval aplikaci pokračovat v práci během procházení grafu objektů, ale fáze Sweep vyžadovala zastavení všech vláken (Stop-The-World). Na zařízeních s malou RAM (512 MB — 1 GB) dosahovaly pauzy 30 ms, což způsobovalo znatelné zpomalení rozhraní. Kromě toho Dalvik nekompaktoval heap, takže po delším provozu rostla fragmentace a přidělování velkých objektů (např. Bitmap) mohlo způsobit OutOfMemoryError i při dostatečném celkovém volném místě.

ART GC

ART (Android Runtime) zavedl generační sběrač s concurrent kompaktací. Heap je rozdělen na tři regiony: Young, Mature (analog Old Generation) a Large Object Space (pro objekty větší než 12 KB). S běr regionu Young probíhá paralelně bez zastavení vláken ve většině případů. V Androidu 10+ se objevilo Concurrent Copying — kompaktace probíhá na pozadí bez Stop-The-World.

Díky architektuře ART se typické pauzy GC snížily na 2–4 ms a ve scénářích s převahou mladých objektů na 0.5–1 ms. To umožnilo zařízením Android poskytovat stabilních 60 FPS i při aktivní práci s pamětí.

Typy sběračů odpadků v Javě

V ekosystému Java existuje několik implementací GC, každá se svým vlastním profilem výkonu. Pro vývoj Android je výběr omezen na ART, ale znalost Java GC je užitečná při psaní serverové části mobilních aplikací a při vývoji s Kotlin Multiplatform.

Serial GC

Serial GC — jednovláknový sběrač s úplným zastavením aplikace (Stop-The-World). Každá operace Mark, Sweep a Compact je provedena jedním vláknem. Výkon je nízký — nepoužívá se pro mobilní servery. Je vhodný pouze pro malé aplikace s heapem do 100 MB.

Parallel GC

Parallel GC (také známý jako Throughput Collector) používá několik vláken pro všechny fáze sběru. Je zaměřen na maximální průchodnost (throughput) — minimalizuje čas strávený v GC vzhledem k času provozu aplikace. Aktivuje se příznakem -XX:+UseParallelGC v JVM.

G1 GC

G1 (Garbage-First) GC — výchozí sběrač v Javě 9+. Heap je rozdělen na regiony o 1–32 MB. G1 předpovídá čas pauzy a snaží se vejít do nastaveného limitu (výchozí 200 ms). Priorita: nejprve se čistí regiony s největším objemem odpadu (odtud název). G1 je účinný pro servery s velkým heapem (4–64 GB) a předvídatelnými pauzami.

java
// Zapnutí G1 GC s cílovou pauzou 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("Prahová hodnota využití heapu překročena: " + used);
            System.out.println("Zvažte snížení alokací");
        }
    }
}

Monitorování heapu prostřednictvím Runtime umožňuje včasnou detekci úniků paměti. Pokud used přesáhne 80% maximálního heapu při stabilním provozu — to je signál možného úniku nebo nadměrné spotřeby paměti aplikací.

Problémy GC a optimalizace paměti v mobilních aplikacích

I moderní ART GC neřeší všechny problémy — nesprávné používání paměti zůstává hlavní příčinou jank a ANR (Application Not Responding). Podívejme se na hlavní scénáře a metody optimalizace.

GC Pauses a Jank

GC Pauses — zastavení vláken aplikace během sběru. Na obrazovce se to projevuje jako vynechané snímky, kdy čas mezi dvěma snímky přesáhne 16.6 ms (60 FPS). Pokud GC trvá 30 ms, vykreslí se pouze jeden snímek místo dvou — uživatel vidí zasekávání rozhraní.

Hlavní příčiny dlouhých pauz: velké množství živých objektů v Old Generation, fragmentace heapu, časté Full GC. Pro diagnostiku se používají Android Studio Profiler a systrace.

Snížení zátěže GC

Hlavním pravidlem GC-friendly kódu — minimalizovat počet alokovaných objektů. Každý nový objekt vyžaduje nejen přidělení paměti, ale i následný sběr. I když je GC rychlý, 1000 zbytečných alokací za sekundu dává 1000 kontrol sběrači.

  • Vyhýbejte se vytváření objektů ve smyčkách — umístěte vytváření mimo smyčku, znovu používejte lokální proměnné
  • Používejte objektové pooly — pro Bitmap, byte[] a další těžké struktury použijte Object Pool nebo RecyclerView.ViewHolder
  • Preferujte primitivy — int místo Integer, float místo Float se vyhýbají autoboxingu
  • Používejte SparseArray — místo HashMap<Integer, V> je v Android SDK k dispozici SparseArray, LongSparseArray, které pracují s primitivy
  • StringBuilder místo konkatenace — každé sčítání řetězců vytváří nový objekt String

Úniky paměti

Únik paměti vzniká, když objekt zůstává dosažitelný, ačkoli již není potřeba. GC nemůže takový objekt odstranit a paměť se postupně vyčerpává. Typické příčiny: neodhlášení posluchači, statické reference na Activity, anonymní třídy zachycující externí kontext a neuzavřené Cursor/InputStream.

java
// Únik paměti: anonymní třída drží referenci na Activity
public void startTask() {
    new Thread(new Runnable() {                    // implicitně drží this (Activity)
        @Override
        public void run() {
            // dlouhá operace...
            System.out.println("Hotovo");
        }
    }).start();
}

// Oprava: statická vnořená třída + 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) {
            // bezpečná práce s Activity
        }
    }
}

V tomto příkladu anonymní Runnable zachycuje implicitní referenci na Activity. Dokud vlákno žije — Activity nemůže být GC shromážděna, i když uživatel již obrazovku zavřel. Oprava WeakReference + static class přeruší tento řetězec a umožní uvolnění Activity.

Často kladené otázky

Čím se liší GC v Androidu od GC v Javě?

GC v Androidu (ART) je generační sběrač s concurrent kompaktací, optimalizovaný pro mobilní zařízení s omezenou pamětí. Java GC (G1, ZGC) jsou serverové sběrače s velkými heapy a předvídatelnými pauzami. ART GC nepoužívá příznaky JVM — veškeré nastavení se provádí automaticky na úrovni OS.

Co je Stop-The-World v GC?

Stop-The-World — okamžik, kdy sběrač zastaví všechna vlákna aplikace, aby bezpečně prošel graf objektů nebo uvolnil paměť. Čím déle STW trvá, tím je jank patrnější. ART zkrátil typickou dobu STW na 2–4 ms díky generační architektuře.

Jak zjistit únik paměti v Androidu?

Použijte Android Studio Memory Profiler — ukazuje růst heapu, počet alokací a umožňuje provést Heap Dump. Pro hloubkovou analýzu použijte LeakCanary — knihovna automaticky detekuje úniky a zobrazuje řetězec referencí bránících sběru GC.

Kdy dochází k Full GC a proč je nebezpečný?

Full GC — úplný sběr všech generací heapu, včetně Old Generation. V mobilních aplikacích může Full GC trvat 50–200 ms, způsobující znatelný jank nebo ANR. Hlavní příčiny: fragmentace heapu, úniky paměti, překročení prahu Old Generation.

Jak Kotlin pomáhá předcházet únikům paměti?

Kotlin poskytuje korutiny se strukturovanou konkurencí — zrušení scope automaticky ruší všechny dceřiné korutiny, čímž předchází únikům. Také v Kotlinu existuje delegát lazy pro líná inicializace a operátory rozsahu viditelnosti, které snižují počet dočasných objektů.

Shrnutí

  • Garbage Collection — automatické řízení paměti odstraněním nedosažitelných objektů, základ Android Runtime
  • Mark-and-Sweep — základní algoritmus s dvoufázovým sběrem, trpí fragmentací heapu
  • Copying Collection — odstraňuje fragmentaci kopírováním živých objektů do kompaktního poloprostoru
  • Generational GC — rozděluje heap na generace (Young/Old), zrychlující sběr mladých krátkoživotních objektů
  • ART v Androidu — generační sběrač s concurrent kompaktací a pauzami 2–4 ms, který nahradil Dalvik v Androidu 5.0
  • Optimalizace GC — snížení alokací, objektové pooly, primitivy místo obalů a SparseArray místo HashMap snižují zátěž sběrače
  • Diagnostika — Android Studio Profiler, systrace a LeakCanary jsou hlavní nástroje pro identifikaci problémů s pamětí

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také