Heap Dump (dumpul heapului) — o imagine a memoriei dinamice a aplicației care conține informații complete despre toate obiectele vii: clasele lor, dimensiunile, referințele mutuale și accesibilitatea de la nodurile rădăcină (GC roots). Heap Dump este instrumentul principal de analiză a scurgerilor de memorie și optimizare a consumului de resurse. Conform Android Developers, analiza heap dumpurilor permite detectarea a până la 95% din scurgerile de memorie, inclusiv referințe ciclice, listeneri uitați și referințe statice neliberate.
Principalele puncte
Heap dump reprezintă un dump complet al heapului mașinii virtuale — zona de memorie unde sunt plasate toate obiectele create dinamic. În Java și Kotlin este Dalvik/ART pe Android, în Swift și Objective-C — heapul gestionat de ARC pe iOS. Heap dump înregistrează fiecare obiect, clasa sa, dimensiunea, câmpurile, referințele către alte obiecte și flagurile de accesibilitate de la GC roots (variabile de stivă, câmpuri statice, referințe JNI).
Scopul principal al heap dumpului este detectarea scurgerilor de memorie. O scurgere apare atunci când aplicația continuă să rețină referințe către obiecte care nu mai sunt necesare, împiedicând colectarea lor de către garbage collector (sau eliberarea prin ARC). Cauze tipice: listeneri de evenimente nederejistrați la distrugerea activity; singletonuri cu referințe la context; closures care capturează self; colecții statice la care se adaugă date fără ștergere. Heap dump oferă o imagine precisă: care obiecte sunt „vii", care sunt inutile și cine exact face referință la ele.
Conform datelor Google I/O, peste 60% din rapoartele de crash ale aplicațiilor Android sunt legate de OutOfMemoryError, iar în 80% din cazuri cauza principală este o scurgere de memorie detectabilă prin heap dump. Pentru aplicațiile iOS situația este similară: scurgerile cauzate de retain cycles sunt una dintre cauzele frecvente de crashuri detectate prin instrumentul Allocations din Xcode.
Heap dumpul trebuie efectuat la următoarele simptome: aplicația consumă memorie liniar la acțiuni repetate (navigare înainte-înapoi între ecrane); după terminarea lucrului unui ecran, memoria nu revine la nivelul inițial; apar OutOfMemoryError sau avertismente memory warning pe iOS; aplicația se termină din cauza depășirii limitei de memorie (EXC_RESOURCE_RESOURCE pe iOS). Colectarea regulată a heap dumpurilor face parte din protocolul culturii inginerești în proiecte mobile mari, precum Instagram și Spotify.
Android Studio oferă Memory Profiler — un instrument încorporat pentru capturarea heap dumpului în timp real. Accesibil prin View → Tool Windows → Profiler. După lansarea aplicației, selectați sesiunea, accesați fila Memory și faceți clic pe Dump Java Heap. Android Studio întrerupe aplicația, execută dumpul heap ART și încarcă rezultatul pentru analiză. Fișierul dump are formatul .hprof — standardul HPROF compatibil cu majoritatea analizoarelor de memorie.
După încărcarea dumpului, Android Studio afișează un tabel de obiecte cu coloanele: Allocations (numărul de instanțe), Native Size (memoria în afara heapului ART), Shallow Size (memoria obiectului însuși), Retained Size (memoria obiectului cu întregul subgraf). Filtrarea după numele clasei, sortarea după retained size și căutarea după pachete permit găsirea rapidă a zonelor problematice.
// Scurgere tipică — listener nederijat în onDestroy
class MainActivity : AppCompatActivity() {
private val sensorManager by lazy {
getSystemService(SENSOR_SERVICE) as SensorManager
}
private val listener = MySensorListener()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
sensorManager.registerListener(listener,
sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT),
SensorManager.SENSOR_DELAY_NORMAL)
}
override fun onDestroy() {
super.onDestroy()
// ❌ A fost omis sensorManager.unregisterListener(listener)
// → Activity nu va fi colectată de GC, heap dump va arăta scurgerea
}
}
Fila Dominator Tree arată obiectele care rețin cea mai mare cantitate de memorie. Dacă un obiect este șters din dominator tree, întreaga memorie pe care o reține devine disponibilă pentru colectare. Acesta este instrumentul cheie: în loc să examinați mii de obiecte, vă concentrați pe 10-20 care controlează 80-90% din memorie. Conform Google, analiza dominator tree este cel mai eficient mod de a găsi punctul de scurgere, reducând timpul de analiză de la ore la minute.
Xcode Instruments oferă două instrumente pentru lucrul cu heap dump: Allocations — capturarea dumpului heap cu graficul consumului în timp real; Leaks — căutarea automată a scurgerilor prin analiza retain cycles. Allocations afișează toate obiectele din heap, dimensiunea lor, numărul de creări (allocations) și eliberări (deallocations). Diferența dintre numărul de creări și eliberări pentru o anumită clasă indică o potențială scurgere.
Capturarea heap dumpului în Allocations se realizează cu butonul Snapshot Memory — instrumentul întrerupe aplicația și execută un dump complet. După aceasta, sunt disponibile vizualizările standard: lista obiectelor pe clase, arborele de apeluri (call tree) pentru fiecare obiect și generatorul de rapoarte. Spre deosebire de Android Studio, Xcode nu folosește .hprof, ci stochează datele în propriul format .trace, compatibil cu Instruments.
// Scurgere tipică iOS — retain cycle prin closure
class NetworkManager {
var onComplete: ((Data) -> Void)?
func startRequest() {
// ❌ Closure capturează self — retain cycle
onComplete = { data in
self.process(data)
}
}
func process(_ data: Data) {}
}
Instrumentul Leaks detectează automat retain cycles și scurgeri prin analiza grafului de referințe. Marchează obiectele cu scurgeri cu o pictogramă violetă și arată calea către rădăcină (GC root). Pentru eliminarea unui retain cycle este suficient să adăugați [weak self] sau [unowned self] în closure. Rularea regulată a instrumentului Leaks este o etapă obligatorie în CI pentru echipele care folosesc Swift pentru dezvoltarea iOS.
// Remediere — referință slabă la self
onComplete = { [weak self] data in
guard let self else { return }
self.process(data)
}
Pentru o analiză corectă a heap dumpului este necesar să înțelegeți trei metrici cheie. Shallow size — volumul de memorie ocupat direct de obiect: câmpurile sale, antetul (header) și alinierea. Pentru un obiect Java/Kotlin tipic, shallow size este de 16-40 de octeți. Retained size — shallow size al obiectului plus shallow size total al tuturor obiectelor care sunt accesibile doar prin acest obiect (adică vor deveni gunoi la ștergerea sa). Exact retained size arată impactul real al obiectului asupra consumului de memorie.
| Metrică | Descriere | Exemplu |
|---|---|---|
| Shallow size | Dimensiunea obiectului însuși în octeți | Bitmap (100×100) = 40 016 B |
| Retained size | Shallow size + tot ce reține | Activity cu View Tree = 2-5 MB |
| Deep size | Retained size + obiecte imbricate din alte grafuri | ScrollView cu adapter = 10-50 MB |
Dominator tree — o structură în care fiecare obiect face referință la „dominatorul" său — obiectul care îi controlează accesibilitatea. Dacă dominatorul este șters, toate obiectele subarborelui său devin gunoi. Analiza dominator tree este cea mai rapidă modalitate de a găsi obiectul care reține cea mai multă memorie. Conform Eclipse MAT (Memory Analyzer Tool), 90% din scurgeri sunt detectate prin examinarea top-20 dominator tree în 5 minute.
Procesul de analiză a unei scurgeri prin heap dump constă din mai mulți pași. Pasul 1: efectuați acțiunea care ar trebui să elibereze memoria (închideți ecranul, finalizați operația). Pasul 2: apelați GC (System.gc() în Android, snapshot forțat în Xcode) și faceți un heap dump. Pasul 3: găsiți obiectele care ar fi trebuit distruse (de exemplu, o instanță Activity după finish). Pasul 4: pentru obiectul suspect, executați Path to GC Roots — lanțul de referințe care menține obiectul viu. Ultima referință din lanț este cauza scurgerii.
Funcția Path to GC Roots este disponibilă în Android Studio Profiler, Eclipse MAT și Xcode Instruments. Afișează cel mai scurt lanț de referințe de la GC root la obiectul problemă. Excluzând referințele slabe (weak) și moi (soft), obțineți doar referințele puternice (strong) — cele care împiedică efectiv colectarea. Conform statisticilor Square Engineering, 70% din scurgerile din aplicațiile Android sunt cauzate de doar două patternuri: referințe statice la Activity sau Context și listeneri înregistrați dar nederejistrați.
// Exemplu de scurgere prin referință statică
object AppCache {
private val cache = mutableMapOf<String, Any>()
fun storeActivityReference(activity: Activity) {
cache["current_activity"] = activity // ❌ Scurgere!
}
}
// Remediere: referință slabă
object AppCacheFixed {
private val cache = mutableMapOf<String, WeakReference<Any>>()
}
Tehnica comparison mode — una dintre cele mai eficiente metode de găsire a scurgerilor. Faceți un heap dump înainte și după o acțiune repetată (de exemplu, cinci navigări pe un ecran și înapoi). Comparați numărul de instanțe ale claselor cheie: dacă numărul de Activity a crescut, deși toate activitățile au fost închise — este o scurgere. Android Studio și Eclipse MAT suportă compararea automată a dumpurilor cu evidențierea diferențelor. Conform Google, compararea dumpurilor permite găsirea scurgerilor invizibile la o analiză unică datorită acumulării efectului.
Pe baza analizei heap dumpurilor în proiecte reale au fost elaborate practici dovedite de optimizare a memoriei. Folosiți WeakReference pentru cache-uri, callbackuri și referințe la context în obiecte cu viață lungă. Derejistrați listenerii în onPause/onDestroy pentru Android și deinit pentru iOS. Evitați colecțiile statice mari — dacă sunt necesare, folosiți LruCache cu limită de dimensiune. Optimizați Bitmapurile: încărcați imaginile cu inSampleSize corect, folosiți Glide sau Picasso cu cache pe disc.
Includeți capturarea regulată a heap dumpului în CI. Configurați o sarcină care rulează teste UI instrumentate, execută scenarii cheie de utilizator și compară heap dumpul cu baseline. Dacă retained size a crescut cu mai mult de 5% față de baseline, buildul este marcat ca regresie. Această abordare este practicată în Airbnb, Uber și alte companii cu cerințe înalte de calitate. Conform Uber Engineering, implementarea analizei automate a heap dumpului în CI a redus numărul de buguri legate de memorie cu 70% într-un trimestru.
// Exemplu de task Gradle pentru heap dump automat în CI
task profileMemory(type: Exec) {
commandLine 'adb', 'shell',
'am start -n com.example/.MainActivity'
// Așteptare încărcare
doLast {
exec { commandLine 'adb', 'shell',
'am broadcast -a com.example.DUMP_HEAP' }
}
}
Întrebări frecvente
Shallow size — dimensiunea obiectului însuși (câmpuri + antet). Retained size — dimensiunea obiectului plus a tuturor obiectelor care vor deveni gunoi la ștergerea sa. Retained size este principalul indicator al impactului obiectului asupra consumului de memorie.
Prin Android Studio Profiler selectați dispozitivul și procesul, apăsați Dump Java Heap. Alternativ — prin linia de comandă: adb shell am dumpheap PID /sdcard/dump.hprof, apoi adb pull.
Heap dump include toate obiectele vii. Dacă aplicația folosește cache-uri, Bitmapuri sau procesează date mari, dumpul poate atinge sute de megaocteți. Filtrați după clase sau folosiți Eclipse MAT pentru încărcarea doar a indexului.
Da, folosiți Eclipse MAT (Memory Analyzer Tool) — un instrument gratuit pentru analiza .hprof. Suportă dominator tree, path to GC roots, compararea dumpurilor și căutarea automată a scurgerilor prin Leak Suspects Report.
Dumpul în sine — da, deoarece colectarea dumpului întrerupe toate firele de execuție (stop-the-world). Fără dump — nu. Faceți dumpul în condiții controlate (mediu de test, CI), nu în producție.
Rezumat
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.
Citiți și