Heap Dump: какво е, анализ на купчината и отстраняване на течове на памет

Автор: IT Sectr Публикувано: 2026-05-07 Време за четене: 10 мин

Heap Dump (dump на купчината) — моментна снимка на динамичната памет на приложението, съдържаща пълна информация за всички живи обекти: техните класове, размери, взаимни референции и достъпност от кореновите възли (GC roots). Heap Dump е основният инструмент за анализ на течове на памет и оптимизация на потреблението на ресурси. Според Android Developers, анализът на heap dumps позволява откриване на до 95% от течовете на памет, включително циклични референции, забравени listeners и неосвободени статични референции.

Основни точки

  • Heap Dump — моментна снимка на цялата динамична памет на приложението с информация за всеки обект и референциите между тях.
  • Android Studio Memory Profiler позволява заснемане на heap dump в реално време за Java и Kotlin приложения.
  • Xcode Instruments предоставя инструмента Allocations за създаване и анализ на heap dumps на iOS/macOS.
  • Shallow и retained size са ключови метрики: shallow — размер на самия обект, retained — размер на обекта плюс всички обекти, които той задържа.
  • Анализът на heap dump включва търсене на dominator tree, най-големи retained objects и най-кратки пътища до GC roots.

Какво е heap dump и защо е необходим

Heap dump представлява пълен dump на купчината (heap) на виртуалната машина — областта от паметта, където се поставят всички динамично създавани обекти. В Java и Kotlin това е Dalvik/ART на Android, в Swift и Objective-C — управляваната от ARC купчина на iOS. Heap dump записва всеки обект, неговия клас, размер, полета, референции към други обекти и флагове за достижимост от GC roots (стекови променливи, статични полета, JNI референции).

Основната цел на heap dump е откриване на течове на памет. Теч възниква, когато приложението продължава да задържа референции към обекти, които вече не са необходими, предотвратявайки тяхното събиране от garbage collector (или освобождаване чрез ARC). Типични причини: слушатели на събития, които не са отписани при унищожаване на activity; сингълтони с референции към контекст; затваряния (closures), които улавят self; статични колекции, към които се добавят данни без изтриване. Heap dump дава точна картина: кои обекти са „живи", кои са излишни и кой точно реферира към тях.

Според данни на Google I/O, над 60% от отчетите за сривове на Android приложения са свързани с OutOfMemoryError, а в 80% от случаите основната причина е теч на памет, откриваем чрез heap dump. За iOS приложения ситуацията е подобна: течовете, причинени от retain cycles, са една от честите причини за сривове, откривани чрез инструмента Allocations в Xcode.

Кога е необходим heap dump

Heap dump трябва да се извърши при следните симптоми: приложението консумира памет линейно при повтарящи се действия (навигиране напред-назад между екрани); след приключване на екрана паметта не се връща към първоначалното ниво; появяват се OutOfMemoryError или предупреждения memory warning на iOS; приложението се прекратява поради превишаване на лимита на паметта (EXC_RESOURCE_RESOURCE на iOS). Редовното събиране на heap dumps е част от протокола на инженерната култура в големи мобилни проекти като Instagram и Spotify.

Heap dump в Android Studio: получаване и анализ

Android Studio предоставя Memory Profiler — вграден инструмент за заснемане на heap dump в реално време. Достъпен чрез View → Tool Windows → Profiler. След стартиране на приложението изберете сесия, отидете на раздела Memory и щракнете върху Dump Java Heap. Android Studio спира приложението, извършва dump на ART купчината и зарежда резултата за анализ. Файлът на dump-а има формат .hprof — стандарт HPROF, съвместим с повечето анализатори на памет.

След зареждане на dump-а Android Studio показва таблица с обекти с колони: Allocations (брой инстанции), Native Size (памет извън ART купчината), Shallow Size (памет на самия обект), Retained Size (памет на обекта с целия подграф). Филтриране по име на клас, сортиране по retained size и търсене по пакети позволяват бързо откриване на проблемни области.

kotlin
// Типичен теч — слушател, неотписан в 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 в Android Studio

Разделът Dominator Tree показва обектите, които задържат най-голямо количество памет. Ако обект бъде изтрит от dominator tree, цялата памет, която той задържа, става достъпна за събиране. Това е ключовият инструмент: вместо да преглеждате хиляди обекти, се фокусирате върху 10-20, които контролират 80-90% от паметта. Според Google, анализът на dominator tree е най-ефективният начин за намиране на точка на теч, съкращаващ времето за анализ от часове до минути.

Heap dump в Xcode Instruments: Allocations и Leaks

Xcode Instruments предоставя два инструмента за работа с heap dump: Allocations — заснемане на dump на купчината с графика на потреблението в реално време; Leaks — автоматично търсене на течове чрез анализ на retain cycles. Allocations показва всички обекти в купчината, техния размер, брой създавания (allocations) и освобождавания (deallocations). Разликата между броя създавания и освобождавания за конкретен клас показва потенциален теч.

Заснемането на heap dump в Allocations се извършва с бутона Snapshot Memory — инструментът спира приложението и прави пълен dump. След това са налични стандартните изгледи: списък на обектите по класове, дърво на извикванията (call tree) за всеки обект и генератор на отчети. За разлика от Android Studio, Xcode не използва .hprof, а съхранява данни в собствен формат .trace, съвместим с Instruments.

swift
// Типичен 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 инструментът автоматично открива retain cycles и течове чрез анализ на референтната графа. Той маркира обектите с теч с лилава икона и показва пътя до корена (GC root). За отстраняване на retain cycle е достатъчно да добавите [weak self] или [unowned self] в затварянето. Редовното стартиране на Leaks инструмента е задължителен етап от CI в екипите, използващи Swift за iOS разработка.

swift
// Поправка — слаба референция към self
onComplete = { [weak self] data in
    guard let self else { return }
    self.process(data)
}

Shallow size, retained size и dominator tree

За коректен анализ на 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 sizeShallow size + всичко, което задържаActivity с View Tree = 2-5 MB
Deep sizeRetained size + вложени обекти от други графиScrollView с адаптер = 10-50 MB

Dominator tree — структура, в която всеки обект реферира към своя „доминатор" — обекта, който контролира неговата достъпност. Ако доминаторът бъде изтрит, всички обекти на неговото поддърво стават боклук. Анализът на dominator tree е най-бързият начин за намиране на обекта, който задържа най-много памет. Според Eclipse MAT (Memory Analyzer Tool), 90% от течовете се откриват чрез преглед на top-20 dominator tree за 5 минути.

Анализ на течове на памет чрез heap dump

Процесът на анализ на теч чрез heap dump се състои от няколко стъпки. Стъпка 1: извършете действието, което трябва да освободи паметта (затворете екрана, завършете операцията). Стъпка 2: извикайте GC (System.gc() в Android, принудителен snapshot в Xcode) и направете heap dump. Стъпка 3: намерете обекти, които е трябвало да бъдат унищожени (напр. инстанция на Activity след finish). Стъпка 4: за подозрителния обект изпълнете Path to GC Roots — веригата от референции, която държи обекта жив. Последната референция във веригата е причината за теча.

Път до GC Roots (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 и регистрирани, но неотписани слушатели.

kotlin
// Пример за теч чрез статична референция
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>>()
}

Сравнение на два heap dump-а

Техниката comparison mode — един от най-ефективните методи за намиране на течове. Направете heap dump преди и след повтарящо се действие (напр. пет навигации до екран и обратно). Сравнете броя инстанции на ключовите класове: ако броят на Activity се е увеличил, въпреки че всички активности са затворени — това е теч. Android Studio и Eclipse MAT поддържат автоматично сравнение на dump-ове с подчертаване на разликите. Според Google, сравнението на dump-ове позволява откриване на течове, невидими при еднократен анализ, благодарение на натрупването на ефекта.

Практически препоръки за намаляване на потреблението на памет

Въз основа на анализ на heap dump-ове в реални проекти са разработени доказани практики за оптимизация на паметта. Използвайте WeakReference за кешове, обратни извиквания и референции към контекст в дългоживеещи обекти. Отписвайте слушателите в 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% за едно тримесечие.

groovy
// Пример за 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?

Shallow size — размерът на самия обект (полета + заглавна част). Retained size — размерът на обекта плюс всички обекти, които ще станат боклук при неговото изтриване. Retained size е основният индикатор за влиянието на обекта върху потреблението на памет.

Как да направим heap dump на физическо Android устройство?

Чрез Android Studio Profiler изберете устройството и процеса, щракнете върху Dump Java Heap. Алтернативно — чрез командния ред: adb shell am dumpheap PID /sdcard/dump.hprof, след това adb pull.

Защо heap dump може да бъде огромен (500 MB+)?

Heap dump включва всички живи обекти. Ако приложението използва кешове, Bitmap-и или обработва големи данни, dump-ът може да достигне стотици мегабайта. Филтрирайте по класове или използвайте Eclipse MAT за зареждане само на индекса.

Може ли да се анализира heap dump без Android Studio?

Да, използвайте Eclipse MAT (Memory Analyzer Tool) — безплатен инструмент за анализ на .hprof. Поддържа dominator tree, path to GC roots, сравнение на dump-ове и автоматично търсене на течове чрез Leak Suspects Report.

Намалява ли heap dump производителността на приложението?

Самият dump — да, защото събирането на dump спира всички нишки (stop-the-world). Без dump — не. Правете dump в контролирани условия (тестова среда, CI), не на продукция.

Резюме

  • Heap Dump — пълна моментна снимка на купчината на приложението с информация за всеки обект и връзките между тях.
  • Android Studio Memory Profiler и Xcode Instruments Allocations — основните инструменти за заснемане на dump-ове.
  • Shallow size — размерът на самия обект; retained size — размерът на обекта с целия подграф от зависимости.
  • Dominator tree — дървото на доминаторите, показващо обектите, които контролират най-много памет.
  • Path to GC Roots — веригата от силни референции, която предотвратява събирането на обекта от garbage collector-а.
  • Сравнение на два heap dump-а (преди/след действие) — най-надеждният метод за откриване на течове.
  • Автоматизация на заснемането и анализа на heap dump-ове в CI предотвратява регресии на паметта в етапа на разработка.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също