Heap Dump: що це таке, аналіз купи та усунення витоків пам'яті

Автор: IT Sectr Опубліковано: 2026-05-07 Час читання: 10 хв

Heap 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 являє собою повний дамп купи (heap) віртуальної машини — області пам'яті, де розміщуються всі динамічно створювані об'єкти. В Java та Kotlin це Dalvik/ART на Android, в Swift та Objective-C — ARC-керована купа на iOS. Heap dump фіксує кожен об'єкт, його клас, розмір, поля, посилання на інші об'єкти та прапорці досяжності від GC roots (стекові змінні, статичні поля, JNI references).

Основна мета heap dump — виявлення витоків пам'яті. Витік виникає, коли додаток продовжує утримувати посилання на об'єкти, які більше не потрібні, запобігаючи їх збиранню збирачем сміття. Типові причини: слухачі подій, не відписані при знищенні activity; синглтони з посиланнями на контекст; замикання (closures), що захоплюють self; статичні колекції, до яких додаються дані без видалення. Heap dump дає точну картину: які об'єкти «живі», які з них зайві та хто саме на них посилається.

За даними Google I/O, більше 60% crash-звітів Android-додатків пов'язані з OutOfMemoryError, і в 80% випадків першопричина — витік пам'яті, що виявляється через heap dump. Для iOS-додатків ситуація аналогічна: витоки через retain cycles — одна з частих причин падінь, що виявляються через Allocations instrument в Xcode.

Коли потрібен heap dump

Heap dump слід виконувати при наступних симптомах: додаток споживає пам'ять лінійно при повторюваних діях; після завершення роботи екрана пам'ять не повертається до початкового рівня; виникають OutOfMemoryError або попередження memory warning на 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 призупинить додаток, виконає дамп купи ART та завантажить результат для аналізу. Файл дампа має формат .hprof — стандарт HPROF, сумісний з більшістю аналізаторів пам'яті.

Після завантаження дампа 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 analysis — найефективніший спосіб знайти точку витоку, що скорочує час аналізу з годин до хвилин.

Heap dump в Xcode Instruments: Allocations та Leaks

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

Захоплення heap dump в Allocations виконується кнопкою Snapshot Memory — інструмент призупиняє додаток та знімає повний дамп. Після цього доступні стандартні представлення: список об'єктів за класами, дерево викликів (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 instrument автоматично виявляє retain cycles та витоки через аналіз графа посилань. Він позначає об'єкти, що витікають, фіолетовим значком та показує шлях до кореня (GC root). Для усунення retain cycle достатньо додати [weak self] або [unowned self] у захоплення замикання. Регулярний прогін Leaks instrument — обов'язковий етап 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 та зробіть heap dump. Крок 3: знайдіть об'єкти, які мали бути знищені. Крок 4: для підозрілого об'єкта виконайте Path to 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 dumps

Техніка comparison mode — один з найефективніших методів пошуку витоків. Зробіть heap dump до та після повторюваної дії. Порівняйте кількість екземплярів ключових класів: якщо кількість Activity зросла, хоча всі активності були закриті — це витік. Android Studio та Eclipse MAT підтримують автоматичне порівняння дампів з виділенням відмінностей. За даними Google, порівняння дампів дозволяє знаходити витоки, невидимі при разовому аналізі, за рахунок накопичення ефекту.

Практичні рекомендації щодо зниження споживання пам'яті

На основі аналізу 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 скоротило кількість memory-related багів на 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'и або обробляє великі дані, дамп може досягати сотень мегабайт. Фільтруйте за класами або використовуйте Eclipse MAT для завантаження тільки індексу.

Чи можна аналізувати heap dump без Android Studio?

Так, використовуйте Eclipse MAT (Memory Analyzer Tool) — безкоштовний інструмент для аналізу .hprof. Підтримує dominator tree, path to GC roots, порівняння дампів та автоматичний пошук витоків через Leak Suspects Report.

Чи знижує heap dump продуктивність додатка?

Сам дамп — так, тому що збирання дампа призупиняє всі потоки (stop-the-world). Без дампа — ні. Робіть дамп у контрольованих умовах (тестовий стенд, CI), не на продакшені.

Підсумки

  • Heap Dump — повний знімок купи додатка з інформацією про кожен об'єкт та зв'язки між ними.
  • Android Studio Memory Profiler та Xcode Instruments Allocations — основні інструменти захоплення дампів.
  • Shallow size — розмір самого об'єкта; retained size — розмір об'єкта з усім підграфом залежностей.
  • Dominator tree — дерево домінаторів, що показує об'єкти, які контролюють найбільшу пам'ять.
  • Path to GC Roots — ланцюжок сильних посилань, що утримує об'єкт від збирання сміття.
  • Порівняння двох heap dumps (до/після дії) — найнадійніший метод виявлення витоків.
  • Автоматизація захоплення та аналізу heap dumps в CI запобігає memory regressions на етапі розробки.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також