OutOfMemoryError — fatální výjimka, která nastává, když Java Virtual Machine (JVM) nebo Android Runtime (ART) nemůže přidělit paměť pro nový objekt kvůli nedostatku místa v haldě (Heap). Podle Square Engineering je 70% OutOfMemoryError v mobilních aplikacích způsobeno úniky paměti, nikoli skutečným překročením limitu. Porozumění příčinám OOM je klíčem ke stabilnímu chodu aplikace.
Hlavní body
OutOfMemoryError (OOM) — je výjimka z rodiny VirtualMachineError v Java/Kotlin, která signalizuje nemožnost přidělit paměť pro nový objekt. Na rozdíl od checked výjimek je OOM Error a nevyžaduje zpracování přes catch — i když ho technicky lze zachytit. Po výskytu OOM je aplikace obvykle v nestabilním stavu a doporučuje se ji ukončit.
Na Androidu má každá aplikace limit Heap nastavený výrobcem zařízení. Pro moderní smartphony s 6+ GB RAM je limit 256–512 MB, pro rozpočtová zařízení — 128–192 MB. Když celkový objem všech živých objektů překročí tento limit, ART vyhodí OutOfMemoryError.
Důležité pochopit: OOM ne vždy znamená, že fyzická paměť zařízení došla. Znamená to, že aplikace vyčerpala svůj limit Heap nastavený systémem. Jiné aplikace mohou mít volnou paměť, ale vaše aplikace ji nemůže použít kvůli izolaci procesů v Androidu.
Pět scénářů pravidelně vede k OOM v mobilních aplikacích. Každý scénář je spojen s určitým typem dat nebo operací.
Bitmap — hlavní spotřebitel paměti v Android aplikacích. Načtení FullHD obrázku (1920 × 1080) v původní velikosti zabírá 8.3 MB ve formátu ARGB_8888. Pokud je v RecyclerView 50 takových obrázků — to je 415 MB, což překračuje Heap jakéhokoli zařízení. Načítání obrázků bez inSampleSize — zaručený OOM na slabých zařízeních.
Použijte Glide nebo Coil pro automatické škálování. Tyto knihovny načítají obrázky ve velikosti odpovídající View, ne původnímu rozlišení. Pro přímé použití BitmapFactory.Options aplikujte inSampleSize: vypočítejte ho jako mocninu dvou tak, aby konečná velikost nepřesáhla 2048 × 2048 pixelů. Dále použijte RGB_565 místo ARGB_8888 pro obrázky bez průhlednosti — to snižuje spotřebu paměti na polovinu.
fun loadScaledBitmap(path: String, reqWidth: Int): Bitmap? {
val opts = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeFile(path, opts)
opts.inSampleSize = calculateSampleSize(opts.outWidth, reqWidth)
opts.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, opts)
}
Jeden únik několika KB nezpůsobí OOM. Ale desítky úniků na každé obrazovce se hromadí: každý přechod na obrazovku přidává únik, GC nemůže uvolnit objekty, Heap se plní. Typický vzor: uživatel otevře a zavře obrazovku profilu 20krát → Heap vzroste o 200 MB → aplikace spadne s OOM.
Nainstalujte LeakCanary do projektu pro automatické odhalování úniků. Zobrazí každý uniklý objekt s přesným stack trace. Po opravě všech úniků se spotřeba Heap stabilizuje: po zavření obrazovky se paměť vrátí na základní úroveň.
Načítání souborů celých do byte[] — přímá cesta k OOM. JSON soubor o 50 MB při parsování vytvoří řetězec stejné velikosti plus DOM model. Video soubory nahrané do paměti, audio buffery a velké protobuf datové sady — všechny mohou překročit limit Heap v jedné operaci.
Zpracovávejte velká data proudy: InputStream s bufferem 4–8 KB, streamovací JSON parser (Jackson nebo Gson s JsonReader), MediaCodec pro video. Nikdy nevolajte File.readBytes() na souborech větších než 10% dostupného Heapu.
Intenzivní vytváření objektů v cyklu bez mezilehlého GC může vést k OOM, zejména na zařízeních s malým Heapem. Příklad: generování 100 000 objektů v for cyklu, které se nevejdou do Heapu, než GC stihne je sesbírat. To je častější ve hrách a grafických editorech.
Použijte Object Pool pro objekty, které jsou hromadně vytvářeny a ničeny. Pro numerická data použijte primitivní typy (FloatArray místo List<Float>). RecyclerView s ViewHolder Pool řeší tento problém pro UI komponenty.
Fragmentace — stav, kdy je volné paměti celkem dost, ale není souvislý blok pro nový objekt. ART kompaktuje Heap při GC, ale ne vždy úspěšně. Velká pole (Bitmap, byte[]) jsou nejcitlivější na fragmentaci.
ART na Android 8+ používá Generational GC, který snižuje fragmentaci oddělením mladých a starých objektů. Nicméně se vyhýbejte alokaci fragmentů různých velikostí ve stejném poolu — snažte se používat předem alokované buffery pevné velikosti.
Limit Heap v Androidu není konstanta — závisí na výrobci, modelu zařízení a verzi OS. Google stanovuje minimální požadavky prostřednictvím Compatibility Definition Document (CDD), ale výrobci nastavují skutečné hodnoty.
| Kategorie zařízení | Typický Heap | largeHeap |
|---|---|---|
| Rozpočtová (1–2 GB RAM) | 128–192 MB | 256–384 MB |
| Střední (3–4 GB RAM) | 256–384 MB | 512 MB |
| Vlajková (6+ GB RAM) | 384–512 MB | 768 MB–1 GB |
| Tablety (4+ GB RAM) | 256–512 MB | 768 MB |
| Wear OS | 32–64 MB | Ne |
Zvýšený limit lze požádat prostřednictvím android:largeHeap="true" v manifestu. Používejte ho opatrně: zvýšení Heap neřeší problém úniků a může zhoršit uživatelský zážitek, pokud je systém nucen zabíjet jiné aplikace pro uvolnění paměti. Pro Wear OS je limit Heap minimální — pouze 32–64 MB, zde largeHeap není k dispozici a úspora paměti je dvojnásob kritická.
Diagnostika OOM vyžaduje analýzu Heap Dump a pochopení, které objekty spotřebovávají paměť. Android Studio poskytuje všechny potřebné nástroje.
Krok 1: Zachyťte okamžik OOM. V Android Memory Profileru stiskněte Record memory allocations a spusťte scénář, který způsobuje pád. Profiler ukáže skok alokací před OOM. Pokud se OOM nereprodukuje, snižte Heap pomocí android:smallHeap v debug sestavení nebo použijte DDMS s ručním voláním GC.
Krok 2: Pořiďte Heap Dump v okamžiku špičkového zatížení (před OOM). Otevřete Dump v Android Studio: karta Classes je seřazena podle Retained Size. Největší objekty — Bitmap, byte[], String. U každého Bitmap zkontrolujte velikost (width × height × 4 byty) a cestu načítání přes Stack Trace.
Krok 3: Analyzujte počet opakujících se objektů. Pokud vidíte 200 stejných Fragment nebo Activity — to je únik. Pokud 500 Bitmap stejné velikosti — problém s cache obrázků. MAT (Memory Analyzer Tool) poskytuje hlubší analýzu s Dominator Tree, ukazující, které objekty drží 80% Heapu.
// Příkaz pro Heap Dump přes adb
adb shell am dumpheap com.example.app /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof.
Komplexní strategie prevence OOM zahrnuje pět úrovní ochrany: od architektonických řešení po monitorování v produkci.
ViewModel + Repository vzor odděluje data od UI a zabraňuje držení View při otáčení obrazovky. ViewModel přežije Activity, jeho data se neztratí a View může být znovu vytvořeno bez duplikování dat v paměti. Použijte StateFlow místo LiveData pro explicitní správu stavů.
Glide — povinná knihovna pro práci s obrázky. Automaticky škáluje, ukládá do cache (disk + paměť) a recykluje Bitmap. Nakonfigurujte diskCacheStrategy a skipMemoryCache pro velké seznamy. Pro animované obrázky použijte Glide s GIF/WebP — zabírají méně paměti než sekvence Bitmap.
Firebase Performance Monitoring sleduje spotřebu paměti v reálném čase. Nastavte alarm na obsazení Heap nad 80% limitu — to je signál pro kontrolu. Crashlytics shromažďuje OOM jako výjimku a ukazuje poslední známý stav Heap před pádem. Pro Android 11+ použijte ApplicationExitInfo pro detekci OOM ukončení.
Povinně testujte aplikaci na zařízeních s minimálním Heap (128–192 MB). Emulátor s malou obrazovkou a malým Heap emuluje rozpočtové zařízení. Pokud aplikace funguje na takovém zařízení, na vlajkových lodích nebudou problémy s OOM. Použijte Firebase Test Lab s reálnými zařízeními různých cenových kategorií.
// Kontrola dostupného Heapu před těžkou operací
fun canAllocate(requiredBytes: Long): Boolean {
val runtime = Runtime.getRuntime()
val free = runtime.freeMemory()
return free > requiredBytes * 2 // rezerva 50%
}
Často kladené otázky
Technicky ano, ale nedoporučuje se. Po OOM je aplikace v nestabilním stavu: nové alokace nemusí fungovat a některé objekty mohou být částečně vytvořeny. Jediný rozumný krok v catch — logování a restart Activity.
Limit Heap je na různých zařízeních různý. Operace, která vyžaduje 300 MB, spadne na zařízení s limitem 192 MB, ale projde na vlajkové lodi s 512 MB. Testujte na zařízeních s minimálními specifikacemi pro odhalení OOM scénářů.
largeHeap zvyšuje limit, ale nezrychluje aplikaci. GC pauzy se prodlužují, protože sběr velkého Heapu trvá déle. Systém může zabíjet aplikace na pozadí pro zajištění paměti. Použijte largeHeap pouze pro aplikace, které objektivně potřebují hodně paměti (kamery, editory).
OOM — výjimka uvnitř aplikace při nedostatku Heap. Systémové zabití (Low Memory Killer) — rozhodnutí Linuxového jádra zabít proces pro uvolnění paměti pro jiné aplikace. Při systémovém zabití aplikace nedostane výjimku — proces prostě skončí.
Vzorec: width × height × bytesPerPixel. ARGB_8888 = 4 B/pixel, RGB_565 = 2 B/pixel. FullHD Bitmap (1920 × 1080) v ARGB_8888 = 8.3 MB. 4K Bitmap (3840 × 2160) = 33 MB. Vždy škálujte obrázky na velikost potřebnou pro zobrazení na obrazovce.
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é