Heap Dump (дамп кучи) — снимок динамической памяти приложения, содержащий полную информацию обо всех живых объектах: их классах, размерах, взаимных ссылках и доступности от корневых узлов (GC roots). Heap Dump — основной инструмент анализа утечек памяти и оптимизации потребления ресурсов. По данным Android Developers, анализ heap dumps позволяет обнаружить до 95% утечек памяти, включая циклические ссылки, забытые listeners и неосвобождённые статические ссылки.
Главное
Heap dump представляет собой полный дамп кучи (heap) виртуальной машины — области памяти, где размещаются все динамически создаваемые объекты. В Java и Kotlin это Dalvik/ART на Android, в Swift и Objective-C — ARC-managed heap на iOS. Heap dump фиксирует каждый объект, его класс, размер, поля, ссылки на другие объекты и флаги достижимости от GC roots (стековые переменные, статические поля, JNI references).
Основная цель heap dump — обнаружение утечек памяти. Утечка возникает, когда приложение продолжает удерживать ссылки на объекты, которые больше не нужны, предотвращая их сборку сборщиком мусора (или освобождение через ARC). Типовые причины: слушатели событий, не отписанные при уничтожении activity; синглтоны с ссылками на контекст; замыкания (closures), захватывающие self; статические коллекции, в которые добавляются данные без удаления. Heap dump даёт точную картину: какие объекты «живы», какие из них лишние и кто именно на них ссылается.
По данным Google I/O, более 60% crash-отчётов Android-приложений связаны с OutOfMemoryError, и в 80% случаев первопричина — утечка памяти, обнаруживаемая через heap dump. Для iOS-приложений ситуация аналогична: утечки из-за retain cycles — одна из частых причин падений, выявляемых через Allocations instrument в Xcode.
Heap dump следует выполнять при следующих симптомах: приложение потребляет память линейно при повторяющихся действиях (переход туда-сюда между экранами); после завершения работы экрана память не возвращается к исходному уровню; возникают OutOfMemoryError или предупреждения memory warning на iOS; приложение terminates из-за превышения лимита памяти (EXC_RESOURCE_RESOURCE на iOS). Регулярный сбор heap dumps — часть протокола инженерной культуры в крупных мобильных проектах, таких как 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 и поиск по пакетам позволяют быстро найти проблемные участки.
// Типовая утечка — слушатель, не отписанный в 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 analysis — самый эффективный способ найти точку утечки, сокращающий время анализа с часов до минут.
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 и зарегистрированные, но не отписанные слушатели.
// Пример утечки через статическую ссылку
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 для кэшей, обратных вызовов и ссылок на контекст в долгоживущих объектах. Отписывайте слушатели в onPause/onDestroy для Android и deinit для iOS. Избегайте больших статических коллекций — если они необходимы, используйте LruCache с ограничением размера. Оптимизируйте Bitmap'ы: загружайте изображения с правильным inSampleSize, используйте Glide или Picasso с дисковым кэшем.
Включите регулярный захват heap dump в CI-пайплайн. Настройте задачу, которая запускает инструментированные тесты UI, выполняет ключевые пользовательские сценарии и сравнивает heap dump с baseline. Если retained size вырос более чем на 5% от baseline — сборка помечается как regression. Такой подход практикуется в Airbnb, Uber и других компаниях с высокими требованиями к качеству. По данным Uber Engineering, внедрение автоматического анализа heap dump в CI сократило количество memory-related багов на 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также