OutOfMemoryError în dezvoltarea aplicațiilor: ce este, cauzele și metode de prevenire

Autor: IT Sectr Publicat: 2026-03-29 Timp de citire: 9 min

OutOfMemoryError — o excepție fatală care apare atunci când mașina virtuală Java (JVM) sau Android Runtime (ART) nu poate aloca memorie pentru un obiect nou din cauza lipsei de spațiu în Heap. Potrivit Square Engineering, 70% din OutOfMemoryError-ürile în aplicațiile mobile sunt cauzate de scurgeri de memorie, nu de depășirea reală a limitei. Înțelegerea cauzelor OOM este cheia funcționării stabile a aplicației.

Principalele puncte

  • OutOfMemoryError — excepție la lipsa Heap pentru crearea unui obiect nou
  • Heap (grămadă) — zona de memorie unde trăiesc toate obiectele Java/Kotlin
  • Bitmap — principalul consumator de Heap în Android, sursa tipică de OOM
  • Heap Dump — instantaneul grămezii pentru a analiza cine și câtă memorie ocupă
  • Tratarea OOM necesită eliminarea scurgerilor și optimizarea consumului de memorie

Ce este OutOfMemoryError

OutOfMemoryError (OOM) — este o excepție din familia VirtualMachineError în Java/Kotlin care semnalează imposibilitatea de a aloca memorie pentru un obiect nou. Spre deosebire de excepțiile checked, OOM este o eroare (Error) și nu necesită gestionare prin catch — deși tehnic poate fi prinsă. După apariția OOM, aplicația se află de obicei într-o stare instabilă și se recomandă să fie închisă.

Pe Android, fiecare aplicație are o limită Heap stabilită de producătorul dispozitivului. Pentru smartphone-urile moderne cu 6+ GB RAM, limita este de 256–512 MB, pentru dispozitivele bugetare — 128–192 MB. Când volumul total al tuturor obiectelor active depășește această limită, ART aruncă OutOfMemoryError.

Important de înțeles: OOM nu înseamnă întotdeauna că memoria fizică a dispozitivului s-a terminat. Înseamnă că aplicația și-a epuizat limita Heap stabilită de sistem. Alte aplicații pot avea memorie liberă, dar aplicația ta nu o poate folosi din cauza izolării proceselor în Android.

Principalele cauze ale OutOfMemoryError

Cinci scenarii duc în mod regulat la OOM în aplicațiile mobile. Fiecare scenariu este legat de un anumit tip de date sau operație.

Bitmap fără scalare

Bitmap — principalul consumator de memorie în aplicațiile Android. Încărcarea unei imagini FullHD (1920 × 1080) în dimensiunea originală ocupă 8.3 MB în format ARGB_8888. Dacă în RecyclerView sunt 50 de astfel de imagini — aceasta înseamnă 415 MB, ceea ce depășește Heap-ul oricărui dispozitiv. Încărcarea imaginilor fără inSampleSize — OOM garantat pe dispozitivele slabe.

Folosește Glide sau Coil pentru scalare automată. Aceste biblioteci încarcă imagini la o dimensiune corespunzătoare View-ului, nu rezoluției originale. Pentru utilizarea directă a BitmapFactory.Options, aplică inSampleSize: calculează-l ca putere a lui doi, astfel încât dimensiunea finală să nu depășească 2048 × 2048 pixeli. Suplimentar, folosește RGB_565 în loc de ARGB_8888 pentru imagini fără transparență — aceasta reduce consumul de memorie la jumătate.

kotlin
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)
}

Scurgeri de memorie (acumulare)

O scurgere de câțiva KB nu va cauza OOM. Dar zeci de scurgeri pe fiecare ecran se acumulează: fiecare tranziție la un ecran adaugă o scurgere, GC nu poate elibera obiectele, Heap se umple. Modelul tipic: utilizatorul deschide și închide ecranul de profil de 20 de ori → Heap crește cu 200 MB → aplicația crapă cu OOM.

Instalează LeakCanary în proiect pentru detectarea automată a scurgerilor. Acesta va arăta fiecare obiect scurs cu un stack trace exact. După remedierea tuturor scurgerilor, consumul Heap devine stabil: după închiderea ecranului, memoria revine la nivelul de bază.

Fișiere mari în memorie

Încărcarea fișierelor în totalitate în byte[] — calea directă către OOM. Un fișier JSON de 50 MB la parsare va crea un șir de aceeași dimensiune plus modelul DOM. Fișierele video încărcate în memorie, bufferele audio și seturile mari de date protobuf — toate pot depăși limita Heap într-o singură operație.

Procesează datele mari cu fluxuri: InputStream cu buffer de 4–8 KB, parser JSON streaming (Jackson sau Gson cu JsonReader), MediaCodec pentru video. Nu apela niciodată File.readBytes() pe fișiere mai mari de 10% din Heap-ul disponibil.

Crearea multor obiecte în buclă

Crearea intensivă de obiecte în buclă fără GC intermediar poate duce la OOM, în special pe dispozitive cu Heap mic. Exemplu: generarea a 100.000 de obiecte într-o buclă for care nu încap în Heap până când GC apucă să le colecteze. Aceasta este mai frecventă în jocuri și editoare grafice.

Folosește Object Pool pentru obiectele care sunt create și distruse în masă. Pentru date numerice, folosește tipuri primitive (FloatArray în loc de List<Float>). RecyclerView cu ViewHolder Pool rezolvă această problemă pentru componentele UI.

Fragmentarea Heap

Fragmentarea — starea în care memoria liberă este suficientă în total, dar nu există un bloc continuu pentru un obiect nou. ART compactează Heap-ul în timpul GC, dar nu întotdeauna cu succes. Tablourile mari (Bitmap, byte[]) sunt cele mai sensibile la fragmentare.

ART pe Android 8+ folosește Generational GC, care reduce fragmentarea prin separarea obiectelor tinere și vechi. Cu toate acestea, evită alocarea de fragmente de dimensiuni diferite în aceeași zonă — încearcă să folosești buffere pre-alocate de dimensiune fixă.

Limitele Heap în Android

Limita Heap în Android nu este o constantă — depinde de producător, modelul dispozitivului și versiunea OS. Google stabilește cerințele minime prin Compatibility Definition Document (CDD), dar producătorii stabilesc valorile reale.

Categoria dispozitivelorHeap tipiclargeHeap
Bugetare (1–2 GB RAM)128–192 MB256–384 MB
Medii (3–4 GB RAM)256–384 MB512 MB
Flagship (6+ GB RAM)384–512 MB768 MB–1 GB
Tablete (4+ GB RAM)256–512 MB768 MB
Wear OS32–64 MBNu

Poți solicita o limită mărită prin android:largeHeap="true" în manifest. Folosește-l cu prudență: creșterea Heap nu rezolvă problema scurgerilor și poate înrăutăți experiența utilizatorului dacă sistemul este forțat să ucidă alte aplicații pentru a elibera memorie pentru a ta. Pentru Wear OS, limita Heap este minimă — doar 32–64 MB, aici largeHeap nu este disponibil, iar economisirea memoriei este dublu de critică.

Diagnosticarea OutOfMemoryError

Diagnosticarea OOM necesită analiza Heap Dump și înțelegerea care obiecte consumă memorie. Android Studio oferă toate instrumentele necesare.

Pasul 1: Prinde momentul OOM. În Android Memory Profiler, apasă Record memory allocations și execută scenariul care cauzează crash-ul. Profilerul va arăta saltul de alocări înainte de OOM. Dacă OOM nu se reproduce, reduce Heap-ul prin android:smallHeap în build-ul de debug sau folosește DDMS cu apel manual GC.

Pasul 2: Fă un Heap Dump în momentul de sarcină maximă (înainte de OOM). Deschide Dump-în Android Studio: fila Classes sortată după Retained Size. Cele mai mari obiecte — Bitmap, byte[], String. Pentru fiecare Bitmap, verifică dimensiunea (width × height × 4 octeți) și calea de încărcare prin Stack Trace.

Pasul 3: Analizează numărul de obiecte repetate. Dacă vezi 200 de Fragment sau Activity identice — este o scurgere. Dacă 500 de Bitmap cu aceeași dimensiune — problemă de cache a imaginilor. MAT (Memory Analyzer Tool) oferă o analiză mai profundă cu Dominator Tree, arătând care obiecte rețin 80% din Heap.

text
// Comanda pentru Heap Dump prin adb
adb shell am dumpheap com.example.app /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof.

Strategii de prevenire a OOM

O strategie complexă de prevenire a OOM include cinci niveluri de protecție: de la soluții arhitecturale până la monitorizare în producție.

Soluții arhitecturale

ViewModel + Repository — modelul separă datele de UI și previne reținerea View-ului la rotirea ecranului. ViewModel supraviețuiește Activity, datele sale nu se pierd, iar View poate fi recreat fără duplicarea datelor în memorie. Folosește StateFlow în loc de LiveData pentru gestionarea explicită a stărilor.

Gestionarea Bitmap și a imaginilor

Glide — biblioteca obligatorie pentru lucrul cu imagini. Aceasta scalează automat, face cache (disc + memorie) și reciclează Bitmap. Configurează diskCacheStrategy și skipMemoryCache pentru liste mari. Pentru imagini animate, folosește Glide cu GIF/WebP — ocupă mai puțină memorie decât o secvență de Bitmap.

Monitorizare în producție

Firebase Performance Monitoring urmărește consumul de memorie în timp real. Setează o alertă la ocuparea Heap de peste 80% din limită — acesta este un semnal de verificare. Crashlytics colectează OOM ca excepție și arată ultima stare cunoscută a Heap-înainte de crash. Pentru Android 11+, folosește ApplicationExitInfo pentru detectarea terminărilor OOM.

Testarea pe dispozitive slabe

Testează obligatoriu aplicația pe dispozitive cu Heap minim (128–192 MB). Emulatorul cu ecran mic și Heap mic simulează un dispozitiv bugetar. Dacă aplicația funcționează pe un astfel de dispozitiv, pe flagship-uri nu vor fi probleme cu OOM. Folosește Firebase Test Lab cu dispozitive reale din diferite categorii de preț.

kotlin
// Verificarea Heap-ului disponibil înainte de o operație grea
fun canAllocate(requiredBytes: Long): Boolean {
    val runtime = Runtime.getRuntime()
    val free = runtime.freeMemory()
    return free > requiredBytes * 2 // rezervă 50%
}

Întrebări frecvente

Se poate prinde OutOfMemoryError prin try-catch?

Tehnic da, dar nu este recomandat. După OOM, aplicația se află într-o stare instabilă: noile alocări pot să nu funcționeze, iar unele obiecte pot fi parțial create. Singura acțiune rezonabilă în catch este logarea și restartarea Activity.

De ce OOM nu apare pe toate dispozitivele?

Limita Heap este diferită pe diferite dispozitive. O operație care necesită 300 MB va cădea pe un dispozitiv cu limită de 192 MB, dar va funcționa pe un flagship cu 512 MB. Testează pe dispozitive cu specificații minime pentru a descoperi scenariile OOM.

Cum afectează largeHeap performanța?

largeHeap crește limita, dar nu accelerează aplicația. Pauzele GC devin mai lungi, deoarece colectarea unui Heap mare durează mai mult. Sistemul poate ucide aplicații în fundal pentru a asigura memoria. Folosește largeHeap doar pentru aplicații care obiectiv au nevoie de multă memorie (camere, editoare).

Cu ce se deosebește OOM de uciderea sistemului a procesului?

OOM — excepție în interiorul aplicației la lipsa Heap. Uciderea sistemului (Low Memory Killer) — decizia nucleului Linux de a ucide un proces pentru a elibera memorie pentru alte aplicații. La uciderea sistemului, aplicația nu primește o excepție — procesul pur și simplu se termină.

Câtă memorie ocupă de fapt un Bitmap?

Formula: width × height × bytesPerPixel. ARGB_8888 = 4 B/pixel, RGB_565 = 2 B/pixel. Bitmap FullHD (1920 × 1080) în ARGB_8888 = 8.3 MB. Bitmap 4K (3840 × 2160) = 33 MB. Scalează întotdeauna imaginile la dimensiunea necesară pentru afișare pe ecran.

Rezumat

  • OutOfMemoryError — excepție fatală la epuizarea limitei Heap a aplicației
  • Bitmap fără scalare — principalul vinovat de OOM în aplicațiile mobile
  • Scurgerile de memorie cauzează 70% din OOM prin acumularea de obiecte la fiecare navigare
  • Limita Heap variază de la 128 MB pe dispozitive bugetare la 512 MB pe flagship-uri
  • Heap Dump cu analiza Retained Size — instrumentul principal de diagnosticare OOM
  • Glide sau Coil sunt obligatorii pentru lucrul cu imagini de orice dimensiune
  • Testarea pe dispozitive cu Heap minim — must-have pentru toate proiectele

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și