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 (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.
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 — 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 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 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.
// 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.
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.
| Charakteristika | Dalvik (do 4.4) | ART (5.0+) |
|---|---|---|
| Typ GC | Mark-and-Sweep s Concurrent Mark | Generační + Concurrent |
| Typická pauza | 10–30 ms | 2–4 ms |
| Kompaktace | Ne (pouze fragmentace roste) | Ano (na pozadí, bez zastavení aplikace) |
| AOT kompilace | JIT (Just-In-Time) | AOT + JIT (hybridní) |
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 (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í.
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 — 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 (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 (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.
// 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í.
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 — 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.
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.
Ú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.
// Ú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
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.
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.
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.
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.
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í
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í.
Přečtěte si také