Mănâncă memorie și se umflă — ce este, cauze și cum să evităm

Autor: IT Sectr Publicat: 2026-07-28 Timp de citire: 10 min

Scurgerea de memorie — una dintre cele mai perfide probleme în dezvoltarea mobilă. Memoria aplicației crește neîncetat până când atinge limita stabilită de sistemul de operare, după care urmează OutOfMemoryError sau terminarea forțată. Conform Square Engineering, aproximativ 40% dintre aplicațiile Android au cel puțin o scurgere de memorie care poate fi detectată doar la profilare. Să analizăm cauzele și metodele de prevenire a creșterii memoriei.

Principalele puncte

  • GC reachability — un obiect nu este șters dacă există o referință activă din setul rădăcină
  • Referințe statice la Activity sau Context — cea mai frecventă cauză de scurgere în Android
  • LeakCanary — instrumentul standard pentru detectarea automată a scurgerilor în Android
  • WeakReference — soluția pentru referințe care nu ar trebui să împiedice colectarea gunoiului
  • Componentele Lifecycle-aware anulează automat abonamentele la distrugerea vizualizării

Ce este scurgerea de memorie și umflarea aplicației?

Scurgerea de memorie (memory leak) — situația în care un obiect care nu mai este necesar aplicației continuă să fie reținut în heap deoarece există o referință activă din setul rădăcină (GC Root). Colectorul de gunoi consideră un astfel de obiect viu și nu îl șterge.

Umflarea memoriei (memory bloat) — o problemă mai largă când aplicația consumă mai multă memorie decât este necesar pentru îndeplinirea sarcinilor curente. Cauze: cache excesiv, duplicarea obiectelor, structuri de date neoptimale și fragmentarea heap-ului.

În Android fiecărei aplicații i se alocă un heap limitat (de obicei 64-512 MB în funcție de dispozitiv și versiunea sistemului de operare). În iOS limitarea este mai puțin strictă, dar sistemul trimite un avertisment de memorie la apropierea de limită.

CaracteristicăAndroidiOS
Limita heap64-512 MB (depinde de dispozitiv)Implicită (sistem)
Colectarea gunoiuluiART (Concurrent, Compact)ARC (Automatic Reference Counting)
Mecanism de scurgereReferințe GC RootCicluri retain (cicluri de referințe puternice)
RezultatOutOfMemoryErrorAvertisment de memorie → terminare

Conform Facebook Engineering Blog, scurgerile de memorie sunt cauza a ~15% din rapoartele de crash în aplicațiile mobile. În Android se adaugă ANR din cauza pauzelor frecvente GC la lipsa de memorie.

Modele tipice de scurgeri de memorie în Android și iOS

Referința statică la Activity — clasica scurgerilor Android. Dacă un câmp static sau un singleton stochează o referință la Activity, aceasta nu va fi colectată de GC nici după finish(), cât timp singletonul este viu. Activity este un obiect greu care conține ierarhia View, resurse și Context.

kotlin
object LeakHolder {
    var activityRef: Activity ?= null // scurgere: referință statică la Activity
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle ?= null) {
        super.onCreate(savedInstanceState)
        LeakHolder.activityRef = this // ❌ MainActivity nu va fi niciodată colectat de GC
    }
}

Clasele anonime și lambda — rețin implicit o referință la clasa exterioară. Dacă un Runnable sau Callback este transmis unui serviciu extern, iar Activity este distrus, obiectul clasei anonime încă atârnă în coadă și nu permite Activity să fie colectat de GC.

  • Handler cu întârziere — dacă Activity este distrus, iar Handler.postDelayed nu a fost încă executat, Activity se scurge
  • Thread și AsyncTask — la rotirea ecranului Activity este recreată, dar Thread-ul vechi continuă să rețină referința la Activity veche
  • Retrofit/Callback — un Callback anonim reține referința la prezentator sau fragment
  • Observatori (Observers) — abonamente LiveData sau RxJava fără dezabonare la onDestroy

În iOS problema principală sunt retain cycles: două obiecte rețin referințe puternice unul la altul, iar ARC nu poate zero contorul de referințe pentru niciunul. Caz tipic: un closure care capturează self puternic, iar self reține referința la closure.

Cum detectăm scurgerile de memorie?

LeakCanary — biblioteca de la Square pentru detectarea automată a scurgerilor în Android. După distrugerea Activity sau Fragment verifică dacă obiectul a fost colectat de GC. Dacă nu — face un heap dump și arată trace-ul scurgerii.

kotlin
// LeakCanary 2.x — auto-integrare prin Application
class ExampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // LeakCanary se instalează automat în build-ul debug
        // prin ContentProvider — configurare zero cod
    }
}

// Apel de verificare forțată
AppWatcher.objectWatcher.watch(watchedObject, "leak description")

Android Studio Profiler — instrumentul încorporat pentru monitorizarea memoriei în timp real. Permite înregistrarea heap dump, găsirea obiectelor suspecte (Retained Size > 1 MB) și urmărirea căii GC root până la fiecare obiect.

Pentru iOS utilizați Xcode Memory Graph Debugger. Acesta vizualizează graful obiectelor în memorie, arată retain cycles și permite detectarea instantanee a referințelor circulare. De asemenea, este disponibil Instruments > Allocations pentru monitorizare pe termen lung.

Strategii de prevenire a scurgerilor

WeakReference — mecanismul de bază pentru referințele care nu ar trebui să împiedice colectarea gunoiului. Dacă GC decide să șteargă obiectul, WeakReference returnează null. Folosit pentru callback-uri, listeneri și referințe la componente UI din firele de fundal.

Componentele Lifecycle-aware — abordarea arhitecturală implementată în Android Jetpack (Lifecycle, LiveData, Flow, coroutines). Abonamentele sunt anulate automat la onDestroy, ceea ce elimină clasa principală de scurgeri.

kotlin
class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<List<User>>()
    val data: LiveData<List<User>> get() = _data

    fun loadData() {
        viewModelScope.launch {
            val result = repository.fetchData()
            _data.postValue(result)
            // corutina se anulează automat la onCleared()
        }
    }
}

viewModelScope și lifecycleScope — CoroutineScope încorporate în Android care sunt anulate la evenimentul corespunzător al ciclului de viață. Aceasta elimină scurgerile prin corutine — cel mai frecvent scenariu în dezvoltarea modernă Android.

  • Nu utilizați referințe statice la Context, Activity, View sau Fragment
  • Anulați toate abonamentele RxJava în disposeBag / CompositeDisposable la onDestroy
  • Utilizați [weak self] / [unowned self] în closure-urile iOS pentru prevenirea retain cycles
  • Verificați Bitmap-urile și obiectele mari — trebuie reciclate sau anulate

Instrumente de profilare a memoriei

Memory Profiler in Android Studio — instrumentul principal pentru monitorizarea heap-ului. Afișează alocări live, instantanee heap, numărul de obiecte pe tipuri. Permite înregistrarea unui dump și analiza acestuia în MAT (Memory Analyzer Tool) pentru găsirea obiectelor suspecte.

Eclipse MAT — analizor desktop de heap dump. După încărcarea fișierului HPROF din Android Studio, MAT construiește arborele dominator, arată retain size al fiecărui obiect și oferă analiză automată a scurgerilor suspecte prin Leak Suspects Report.

Xcode Memory Graph — debugger vizual pentru retain cycles. La apăsarea butonului Memory Graph Debugger, Xcode oprește aplicația, construiește graful complet al obiectelor în memorie și evidențiază retain cycles cu roșu.

InstrumentPlatformăCaracteristică
LeakCanaryAndroidDetectare automată a scurgerilor după destroy
Memory ProfilerAndroid StudioHeap dump + alocări live
Eclipse MATAndroidArbore dominator, Leak Suspects Report
Memory GraphiOS (Xcode)Vizualizator retain cycles

Conform Google I/O 2023, aplicațiile care folosesc LeakCanary în build-urile de debug reduc numărul de crash-uri legate de memorie cu 30-50% în primele 2 luni după implementare. Se recomandă adăugarea LeakCanary în faza de onboarding a proiectului.

Întrebări frecvente

Cu ce diferă scurgerea de memorie de umflare?

Scurgerea — obiecte inaccesibile codului, dar neșterse de GC din cauza referințelor active. Umflarea — aplicația reține în memorie obiecte care sunt logic necesare, dar în cantitate excesivă (de exemplu, cache de 50 MB la o aplicație care rulează de 80 MB). Umflarea se tratează arhitectural, scurgerea — prin gestionarea corectă a referințelor.

Cum detectează LeakCanary scurgerile?

LeakCanary folosește ObjectWatcher — după onDestroy() Activity creează un WeakReference pe Activity și pornește GC. Dacă după 5 secunde WeakReference nu este curățat, LeakCanary face un heap dump, analizează cel mai scurt lanț de referințe de la GC Root la obiect și arată stiva exactă a scurgerii cu numele fișierului și linia de cod.

De ce Bitmap provoacă adesea OutOfMemoryError?

Bitmap ocupă memorie în afara heap-ului Java, în memoria nativă (native heap). Dimensiunea unui Bitmap = lățime × înălțime × 4 octeți (ARGB_8888). O fotografie de 12 MP (4000×3000) ocupă 48 MB. Android nu poate întotdeauna elibera la timp memoria nativă, ceea ce la acumularea mai multor Bitmap-uri duce la OOM chiar și cu suficient heap Java.

Ce este un retain cycle în iOS?

Retain cycle — o situație în ARC când două obiecte rețin referințe puternice unul la altul, iar contorul de referințe nu ajunge niciodată la zero. Exemplu tipic: ViewController cu o referință puternică la un closure, iar closure capturează self puternic. Soluție: folosiți [weak self] sau [unowned self] în closure-uri.

Care este dimensiunea maximă a heap-ului pe Android?

Dimensiunea heap-ului depinde de dispozitiv și versiunea Android. Pentru dispozitivele vechi (API 15-24) — 64-128 MB. Pentru cele moderne (API 25+) — 256-512 MB. Valoarea exactă poate fi obținută prin ActivityManager.getMemoryClass(). Pentru aplicații mari (jocuri, editoare) există largeHeap=true în manifest, care oferă până la 1 GB.

Rezumat

  • Scurgerea de memorie — obiect neșters de GC din cauza unei referințe active din setul rădăcină; umflarea — consum excesiv de memorie fără scurgeri evidente
  • Referințele statice la Activity, Context, View — numărul unu printre cauzele scurgerilor în Android; soluția — WeakReference sau Application Context
  • Clasele anonime și lambda rețin implicit referința la clasa exterioară; callback-urile neanulate — a doua cea mai frecventă cauză
  • LeakCanary — standardul de detectare automată a scurgerilor în Android; integrarea durează 5 minute și reduce rata de crash cu 30-50%
  • lifecycleScope și viewModelScope anulează automat corutinele la destroy, eliminând o întreagă clasă de scurgeri
  • Retain cycles în iOS se rezolvă prin weak/unowned self în closure-uri și delegate
  • Profilați memoria cel puțin o dată pe sprint — heap dump cu MAT sau Memory Graph ar trebui să fie parte din code review

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