Heap Dump (дамп хеапа) — снимак динамичке меморије апликације који садржи потпуне информације о свим живим објектима: њиховим класама, величинама, међусобним референцама и доступности од коренских чворова (GC roots). Heap Dump је основни алат за анализу цурења меморије и оптимизацију потрошње ресурса. Према Android Developers, анализа heap dump-ова омогућава откривање до 95% цурења меморије, укључујући цикличне референце, заборављене listener-е и неослобођене статичке референце.
Главно
Heap dump представља потпун дамп хеапа (heap) виртуелне машине — области меморије где се смештају сви динамички креирани објекти. У Java и Kotlin-у то је Dalvik/ART на Android-у, у Swift и Objective-C-у — ARC-управљани хеап на iOS-у. Heap dump бележи сваки објекат, његову класу, величину, поља, референце на друге објекте и заставице доступности од GC roots-а (стек променљиве, статичка поља, JNI референце).
Основни циљ heap dump-а је откривање цурења меморије. Цурење настаје када апликација наставља да држи референце ка објектима који више нису потребни, спречавајући њихово сакупљање од стране garbage collector-а (или ослобађање путем ARC-а). Типични узроци: listener-и догађаја који нису одјављени при уништењу activity; синглтони са референцама на контекст; затварања (closures) која хватају self; статичке колекције у које се додају подаци без брисања. Heap dump даје прецизну слику: који објекти су „живи“, који су сувишни и ко тачно референцира на њих.
Према подацима Google I/O, више од 60% извештаја о падовима Android апликација је повезано са OutOfMemoryError, а у 80% случајева основни узрок је цурење меморије које се открива путем heap dump-а. За iOS апликације ситуација је слична: цурења узрокована retain cycles-има су један од честих узрока падова који се откривају кроз Allocations instrument у Xcode-у.
Heap dump треба извршити код следећих симптома: апликација троши меморију линеарно при понављајућим радњама (прелажење напред-назад између екрана); након завршетка рада екрана меморија се не враћа на почетни ниво; јављају се OutOfMemoryError или упозорења memory warning на iOS-у; апликација се прекида због прекорачења ограничења меморије (EXC_RESOURCE_RESOURCE на iOS-у). Редовно прикупљање heap dump-ова је део протокола инжењерске културе у великим мобилним пројектима, попут Instagram-а и Spotify-ја.
Android Studio пружа Memory Profiler — уграђени алат за снимање heap dump-а у реалном времену. Доступан је путем View → Tool Windows → Profiler. Након покретања апликације, изаберите сесију, пређите на картицу Memory и кликните Dump Java Heap. Android Studio зауставља апликацију, изводи дамп ART хеапа и учитава резултат за анализу. Датотека дампа има формат .hprof — стандард HPROF компатибилан са већином анализатора меморије.
Након учитавања дампа, Android Studio приказује табелу објеката са колонама: Allocations (број инстанци), Native Size (меморија ван ART хеапа), Shallow Size (меморија самог објекта), Retained Size (меморија објекта са целим подграфом). Филтрирање по имену класе, сортирање по retained size и претрага по пакетима омогућавају брзо проналажење проблематичних области.
// Типично цурење — listener није одјављен у 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()
// ❌ Пропуштен sensorManager.unregisterListener(listener)
// → Activity неће бити сакупљена од стране GC-а, heap dump ће показати цурење
}
}
Картица Dominator Tree приказује објекте који држе највећу количину меморије. Ако се објекат избрише из dominator tree-а, сва меморија коју држи постаје доступна за сакупљање. Ово је кључни алат: уместо прегледа хиљада објеката, фокусирате се на 10–20 који контролишу 80–90% меморије. Према Google-у, анализа dominator tree-а је најефикаснији начин за проналажење тачке цурења, смањујући време анализе са сати на минуте.
Xcode Instruments пружа два алата за рад са heap dump-ом: Allocations — снимање дампа хеапа са графиконом потрошње у реалном времену; Leaks — аутоматско проналажење цурења анализом retain cycles-а. Allocations приказује све објекте у хеапу, њихову величину, број креирања (allocations) и ослобађања (deallocations). Разлика између броја креирања и ослобађања за одређену класу указује на потенцијално цурење.
Снимање heap dump-а у Allocations-у се врши дугметом Snapshot Memory — алат зауставља апликацију и снима потпун дамп. Након тога доступни су стандардни прикази: листа објеката по класама, стабло позива (call tree) за сваки објекат и генератор извештаја. За разлику од Android Studio-а, Xcode не користи .hprof, већ чува податке у сопственом формату .trace, компатибилном са Instruments-ом.
// Типично iOS цурење — retain cycle кроз затварање
class NetworkManager {
var onComplete: ((Data) -> Void)?
func startRequest() {
// ❌ Затварање хвата self — retain cycle
onComplete = { data in
self.process(data)
}
}
func process(_ data: Data) {}
}
Leaks instrument аутоматски детектује retain cycles и цурења анализом графа референци. Означава објекте који цуре љубичастом иконом и приказује пут до корена (GC root). За отклањање retain cycle-а довољно је додати [weak self] или [unowned self] у затварање. Редовно покретање Leaks instrument-а је обавезна фаза CI-а у тимовима који користе Swift за iOS развој.
// Исправка — слаба референца на self
onComplete = { [weak self] data in
guard let self else { return }
self.process(data)
}
За коректну анализу heap dump-а потребно је разумети три кључне метрике. Shallow size — обим меморије коју директно заузима објекат: његова поља, заглавље (header) и поравнање. За типичан Java/Kotlin објекат shallow size износи 16–40 бајтова. Retained size — shallow size објекта плус укупан shallow size свих објеката који су доступни само кроз овај објекат (односно постаће смеће при његовом уклањању). Управо retained size показује стварни утицај објекта на потрошњу меморије.
| Метрика | Опис | Пример |
|---|---|---|
| Shallow size | Величина самог објекта у бајтовима | Bitmap (100×100) = 40 016 B |
| Retained size | Shallow size + све што држи | Activity са View Tree = 2–5 MB |
| Deep size | Retained size + угнежђени објекти из других графова | ScrollView са адаптером = 10–50 MB |
Dominator tree — структура у којој сваки објекат упућује на свог „доминатора“ — објекат који контролише његову доступност. Ако се доминатор избрише, сви објекти његовог подстабла постају смеће. Анализа dominator tree-а је најбржи начин за проналажење објекта који држи највише меморије. Према Eclipse MAT-у (Memory Analyzer Tool), 90% цурења се открива прегледом top-20 dominator tree-а за 5 минута.
Процес анализе цурења путем heap dump-а састоји се од неколико корака. Корак 1: извршите радњу која би требало да ослободи меморију (затворите екран, завршите операцију). Корак 2: позовите GC (System.gc() у Android-у, принудни snapshot у Xcode-у) и направите heap dump. Корак 3: пронађите објекте који су требали бити уништени (нпр. инстанца Activity након finish). Корак 4: за сумњиви објекат извршите Path to GC Roots — ланац референци који држи објекат живим. Последња референца у ланцу је узрок цурења.
Функција Path to GC Roots је доступна у Android Studio Profiler-у, Eclipse MAT-у и Xcode Instruments-у. Приказује најкраћи ланац референци од GC root-а до проблематичног објекта. Искључивањем слабих (weak) и меких (soft) референци добијате само јаке (strong) — оне које заиста спречавају сакупљање. Према статистици Square Engineering-а, 70% цурења у Android апликацијама изазвано је са само два обрасца: статичке референце на Activity или Context и регистровани али неодјављени listener-и.
// Пример цурења кроз статичку референцу
object AppCache {
private val cache = mutableMapOf<String, Any>()
fun storeActivityReference(activity: Activity) {
cache["current_activity"] = activity // ❌ Цурење!
}
}
// Исправка: слаба референца
object AppCacheFixed {
private val cache = mutableMapOf<String, WeakReference<Any>>()
}
Техника comparison mode — један од најефикаснијих метода за проналажење цурења. Направите heap dump пре и после понављајуће радње (нпр. пет прелазака на екран и назад). Упоредите број инстанци кључних класа: ако се број Activity повећао, иако су све активности затворене — то је цурење. Android Studio и Eclipse MAT подржавају аутоматско поређење дампова са истицањем разлика. Према Google-у, поређење дампова омогућава проналажење цурења невидљивих при једнократној анализи захваљујући акумулацији ефекта.
На основу анализе heap dump-ова у реалним пројектима развијене су проверене праксе оптимизације меморије. Користите WeakReference за кешеве, повратне позиве и референце на контекст у дугоживећим објектима. Одјављујте listener-е у onPause/onDestroy за Android и deinit за iOS. Избегавајте велике статичке колекције — ако су неопходне, користите LruCache са ограничењем величине. Оптимизујте Bitmap-ове: учитавајте слике са одговарајућим inSampleSize, користите Glide или Picasso са дисковним кешом.
Укључите редовно снимање heap dump-а у CI. Подесите задатак који покреће инструментиране UI тестове, извршава кључне корисничке сценарије и упоређује heap dump са baseline-ом. Ако retained size порасте за више од 5% у односу на baseline, изградња се означава као регресија. Овај приступ се практикује у Airbnb-у, Uber-у и другим компанијама са високим захтевима за квалитетом. Према Uber Engineering-у, увођење аутоматске анализе heap dump-а у CI смањило је број багова везаних за меморију за 70% у тромесечју.
// Пример Gradle задатка за аутоматски heap dump у CI-ју
task profileMemory(type: Exec) {
commandLine 'adb', 'shell',
'am start -n com.example/.MainActivity'
// Чекање учитавања
doLast {
exec { commandLine 'adb', 'shell',
'am broadcast -a com.example.DUMP_HEAP' }
}
}
Често постављана питања
Shallow size — величина самог објекта (поља + заглавље). Retained size — величина објекта плус свих објеката који ће постати смеће при његовом уклањању. Retained size је главни показатељ утицаја објекта на потрошњу меморије.
Кроз Android Studio Profiler изаберите уређај и процес, кликните Dump Java Heap. Алтернативно — преко командне линије: adb shell am dumpheap PID /sdcard/dump.hprof, затим adb pull.
Heap dump укључује све живе објекте. Ако апликација користи кешеве, Bitmap-ове или обрађује велике податке, дамп може достићи стотине мегабајта. Филтрирајте по класама или користите Eclipse MAT за учитавање само индекса.
Да, користите Eclipse MAT (Memory Analyzer Tool) — бесплатан алат за анализу .hprof датотека. Подржава dominator tree, path to GC roots, поређење дампова и аутоматско проналажење цурења путем Leak Suspects Report-а.
Сам дамп — да, јер прикупљање дампа зауставља све нити (stop-the-world). Без дампа — не. Правите дамп у контролисаним условима (тест окружење, CI), не на продукцији.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође