OutOfMemoryError — egy végzetes kivétel, amely akkor keletkezik, amikor a Java Virtual Machine (JVM) vagy Android Runtime (ART) nem tud memóriát foglalni egy új objektum számára a Heap-ben lévő hely hiánya miatt. A Square Engineering szerint a mobilalkalmazásokban előforduló OutOfMemoryError-ok 70%-át memóriaszivárgások okozzák, nem a tényleges határtúllépés. Az OOM okainak megértése a kulcs az alkalmazás stabil működéséhez.
Főbb pontok
OutOfMemoryError (OOM) — a VirtualMachineError családjába tartozó kivétel Java/Kotlin nyelven, amely azt jelzi, hogy nem lehet memóriát foglalni egy új objektum számára. A checked kivételekkel ellentétben az OOM egy Error, és nem igényel catch-en keresztüli kezelést — bár technikailag elkapható. Az OOM fellépése után az alkalmazás általában instabil állapotban van, és javasolt bezárni.
Androidon minden alkalmazásnak van az eszköz gyártója által beállított Heap-korlátja. A 6+ GB RAM-mal rendelkező modern okostelefonok esetében a korlát 256–512 MB, az olcsóbb eszközök esetében — 128–192 MB. Amikor az összes élő objektum össztérfogata meghaladja ezt a korlátot, az ART OutOfMemoryError-t dob.
Fontos megérteni: az OOM nem mindig jelenti azt, hogy az eszköz fizikai memóriája elfogyott. Azt jelenti, hogy az alkalmazás kimerítette a rendszer által beállított Heap-korlátját. Más alkalmazásoknak lehet szabad memóriájuk, de az Ön alkalmazása nem használhatja azt az Android folyamatizolációja miatt.
Öt forgatókönyv vezet rendszeresen OOM-hoz mobilalkalmazásokban. Minden forgatókönyv egy adott adattípushoz vagy művelethez kapcsolódik.
Bitmap — a fő memóriafogyasztó Android-alkalmazásokban. Egy FullHD kép (1920 × 1080) betöltése eredeti méretben 8.3 MB-ot foglal ARGB_8888 formátumban. Ha 50 ilyen kép van egy RecyclerView-ban — az 415 MB, ami meghaladja bármely eszköz Heap-jét. Képek betöltése inSampleSize nélkül — garantált OOM gyenge eszközökön.
Használjon Glide-ot vagy Coil-t az automatikus méretezéshez. Ezek a könyvtárak a View-hoz igazított méretben töltik be a képeket, nem az eredeti felbontásban. A BitmapFactory.Options közvetlen használatához alkalmazza az inSampleSize-t: számítsa ki két hatványaként úgy, hogy a végső méret ne haladja meg a 2048 × 2048 pixelt. Ezenkívül átlátszóság nélküli képekhez használjon RGB_565-öt ARGB_8888 helyett — ez felezi a memóriafogyasztást.
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)
}
Egy pár KB-os szivárgás nem okoz OOM-ot. De tucatnyi szivárgás minden képernyőn felhalmozódik: minden átmenet egy képernyőre hozzáad egy szivárgást, a GC nem tudja felszabadítani az objektumokat, a Heap megtelik. Tipikus mintázat: a felhasználó 20-szor nyitja meg és zárja be a profil képernyőt → a Heap 200 MB-tal nő → az alkalmazás OOM-mal összeomlik.
Telepítse a LeakCanary-t a projektbe az automatikus szivárgásészleléshez. Ez minden kiszivárgott objektumot megjelenít a pontos stack trace-szel. Az összes szivárgás kijavítása után a Heap használata stabillá válik: a képernyő bezárása után a memória visszatér az alapszintre.
Fájlok teljes egészében byte[]-ba töltése — közvetlen út az OOM-hoz. Egy 50 MB-os JSON fájl elemzéskor egy ugyanolyan méretű stringet és DOM modellt hoz létre. A memóriába töltött videofájlok, audio puffer és nagy protobuf adathalmazok — mindegyik túllépheti a Heap korlátot egyetlen művelettel.
Dolgozza fel a nagy adatokat folyamokkal: InputStream 4–8 KB pufferrel, streaming JSON parser (Jackson vagy Gson JsonReader-rel), MediaCodec videóhoz. Soha ne hívja a File.readBytes() metódust a rendelkezésre álló Heap 10%-nál nagyobb fájlokon.
Intenzív objektumlétrehozás ciklusban köztes GC nélkül OOM-hoz vezethet, különösen kis Heap-pel rendelkező eszközökön. Példa: 100.000 objektum generálása egy for ciklusban, amelyek nem férnek el a Heap-ben, mielőtt a GC össze tudná gyűjteni őket. Ez gyakoribb játékokban és grafikus szerkesztőkben.
Használjon Object Pool-t azokhoz az objektumokhoz, amelyeket tömegesen hoz létre és semmisít meg. Numerikus adatokhoz használjon primitív típusokat (FloatArray a List<Float> helyett). A RecyclerView ViewHolder Pool-lal megoldja ezt a problémát az UI komponensek számára.
Fragmentáció — állapot, amikor összességében elegendő szabad memória van, de nincs folytonos blokk egy új objektum számára. Az ART a GC során tömöríti a Heap-et, de nem mindig sikeresen. A nagyméretű tömbök (Bitmap, byte[]) a legérzékenyebbek a fragmentációra.
Az ART Android 8+-on Generational GC-t használ, amely a fiatal és öreg objektumok szétválasztásával csökkenti a fragmentációt. Ennek ellenére kerülje a különböző méretű fragmensek egy medencében történő lefoglalását — próbáljon előre lefoglalt, rögzített méretű puffereket használni.
A Heap korlát Androidon nem állandó — függ a gyártótól, az eszköz modelljétől és az OS verziójától. A Google a Compatibility Definition Document (CDD) segítségével határozza meg a minimális követelményeket, de a gyártók állítják be a tényleges értékeket.
| Eszközkategória | Tipikus Heap | largeHeap |
|---|---|---|
| Olcsó (1–2 GB RAM) | 128–192 MB | 256–384 MB |
| Közepes (3–4 GB RAM) | 256–384 MB | 512 MB |
| Csúcs (6+ GB RAM) | 384–512 MB | 768 MB–1 GB |
| Tabletek (4+ GB RAM) | 256–512 MB | 768 MB |
| Wear OS | 32–64 MB | Nincs |
Növelt korlátot kérhet az android:largeHeap="true" segítségével a manifesztumban. Használja óvatosan: a Heap növelése nem oldja meg a szivárgási problémát, és ronthatja a felhasználói élményt, ha a rendszer kényszerül más alkalmazásokat leállítani a memória felszabadítása érdekében. Wear OS esetén a Heap korlát minimális — mindössze 32–64 MB, itt a largeHeap nem érhető el, és a memóriamegtakarítás kétszeresen kritikus.
Az OOM diagnosztizálása Heap Dump elemzést és annak megértését igényli, hogy mely objektumok fogyasztanak memóriát. Az Android Studio minden szükséges eszközt biztosít.
1. lépés: Fogja el az OOM pillanatát. Az Android Memory Profilerben nyomja meg a Record memory allocations gombot, és hajtsa végre az összeomlást okozó forgatókönyvet. A profiler megmutatja az allokációk ugrását az OOM előtt. Ha az OOM nem reprodukálható, csökkentse a Heap-et az android:smallHeap segítségével debug építésben, vagy használja a DDMS-t kézi GC-hívással.
2. lépés: Készítsen Heap Dump-ot a csúcs terhelés pillanatában (az OOM előtt). Nyissa meg a Dump-ot az Android Studio-ban: a Classes fül a Retained Size szerint van rendezve. A legnagyobb objektumok — Bitmap, byte[], String. Minden Bitmap esetében ellenőrizze a méretet (width × height × 4 bájt) és a betöltési útvonalat a Stack Trace-en keresztül.
3. lépés: Elemezze az ismétlődő objektumok számát. Ha 200 azonos Fragmentet vagy Activity-t lát — az szivárgás. Ha 500 azonos méretű Bitmap — képgyorsítótár-probléma. A MAT (Memory Analyzer Tool) mélyebb elemzést biztosít Dominator Tree-vel, megmutatva, hogy mely objektumok tartják a Heap 80%-t.
// Parancs a Heap Dump-hoz adb-n keresztül
adb shell am dumpheap com.example.app /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof.
Egy átfogó OOM-megelőzési stratégia öt védelmi szintet foglal magában: az architekturális megoldásoktól a éles célú monitorozásig.
ViewModel + Repository minta elkülöníti az adatokat az UI-tól, és megakadályozza a View megtartását képernyő forgatásakor. A ViewModel túlléli az Activity-t, adatai nem vesznek el, és a View újra létrehozható anélkül, hogy az adatok megkettőződénének a memóriában. Használjon StateFlow-t a LiveData helyett az állapotok explicit kezeléséhez.
Glide — kötelező könyvtár a képekkel való munkához. Automatikusan méretez, gyorsítótáraz (lemez + memória) és újrahasznosítja a Bitmap-et. Konfigurálja a diskCacheStrategy és skipMemoryCache beállításokat nagy listákhoz. Animált képekhez használja a Glide-ot GIF/WebP-vel — ezek kevesebb memóriát foglalnak, mint a Bitmap sorozatok.
Firebase Performance Monitoring valós időben követi a memóriafogyasztást. Állítson be riasztást, ha a Heap több mint 80%-ban használt — ez egy ellenőrzési jel. A Crashlytics OOM-ot kivételként gyűjti, és megmutatja az utolsó ismert Heap állapotot az összeomlás előtt. Android 11+ esetén használja az ApplicationExitInfo-t az OOM-befejezések észleléséhez.
Kötelezően tesztelje az alkalmazást minimális Heap-pel (128–192 MB) rendelkező eszközökön. A kis képernyős és kis Heap-pel rendelkező emulátor egy olcsó eszközt szimulál. Ha az alkalmazás működik egy ilyen eszközön, a csúcseszközökön nem lesz OOM-probléma. Használja a Firebase Test Lab-ot különböző árkategóriájú valós eszközökkel.
// Rendelkezésre álló Heap ellenőrzése nehéz művelet előtt
fun canAllocate(requiredBytes: Long): Boolean {
val runtime = Runtime.getRuntime()
val free = runtime.freeMemory()
return free > requiredBytes * 2 // 50% tartalék
}
Gyakran Ismételt Kérdések
Technikailag igen, de nem ajánlott. OOM után az alkalmazás instabil állapotban van: az új allokációk nem működhetnek, és egyes objektumok részben létrehozottak lehetnek. Az egyetlen ésszerű művelet a catch-ben — naplózás és az Activity újraindítása.
A Heap korlát különböző a különböző eszközökön. Egy 300 MB-ot igénylő művelet összeomlik egy 192 MB korlátú eszközön, de működik egy 512 MB-os csúcseszközön. Teszteljen minimális specifikációjú eszközökön az OOM-forgatókönyvek felfedezéséhez.
largeHeap növeli a korlátot, de nem gyorsítja fel az alkalmazást. A GC szünetek hosszabbak lesznek, mert a nagy Heap összegyűjtése több ideig tart. A rendszer leállíthat hättéralkalmazásokat a memória biztosítása érdekében. Használja a largeHeap-ot csak azokhoz az alkalmazásokhoz, amelyek objektíven sok memóriát igényelnek (kamerák, szerkesztők).
OOM — kivétel az alkalmazáson belül, ha nincs elég Heap. A rendszer általi gyilkosság (Low Memory Killer) — a Linux kernel döntése, hogy megöl egy folyamatot a memória felszabadítása érdekében más alkalmazások számára. Rendszer általi gyilkosságnál az alkalmazás nem kap kivételt — a folyamat egyszerűen véget ér.
Képlet: width × height × bytesPerPixel. ARGB_8888 = 4 B/pixel, RGB_565 = 2 B/pixel. FullHD Bitmap (1920 × 1080) ARGB_8888-ban = 8.3 MB. 4K Bitmap (3840 × 2160) = 33 MB. Mindig méretezze át a képeket a képernyőn történő megjelenítéshez szükséges méretre.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is