Android

Профилирование памяти Android: полное руководство по Android Studio

В этом гайде вы научитесь использовать встроенный Profiler в Android Studio для анализа использования памяти, обнаружения утечек и оптимизации работы вашего Android-приложения.

Обновлено 15 февраля 2026 г.
15-20 мин
Средняя
FixPedia Team
Применимо к:Android Studio Hedgehog (2023.1.1)+Android 5.0 (API 21)+Kotlin 1.8+ / Java 11+

Введение / Зачем это нужно

Профилирование памяти — ключевой этап в разработке стабильных Android-приложений. Утечки памяти (memory leaks) приводят к постепенному росту потребления RAM, замедлению работы, а в худшем случае — к крашу с ошибкой OutOfMemoryError. Встроенный Memory Profiler в Android Studio позволяет визуально отслеживать распределение памяти в реальном времени, делать снимки состояния кучи (Heap Dump) и находить объекты, которые не должны находиться в памяти. Этот гайд проведёт вас через весь процесс от запуска инструмента до интерпретации результатов.

Требования / Подготовка

Перед началом убедитесь, что:

  1. У вас установлен Android Studio версии Hedgehog (2023.1.1) или новее. Инструмент постоянно улучшается.
  2. Устройство или эмулятор работает на Android 5.0 (API 21) или выше.
  3. Приложение собрано в debug-конфигурации (с включённой отладочной информацией). Для release-сборок анализ возможен, но сложнее.
  4. В build.gradle (module) у вашего приложения включён debuggable true для debug-сборок (обычно это по умолчанию).
  5. Устройство подключено по USB с включённой отладкой по USB, или запущен эмулятор.

Шаг 1: Запустите Profiler и подключите устройство

  1. Запустите ваше приложение на устройстве или эмуляторе из Android Studio (кнопка Run).
  2. Откройте окно Profiler: View → Tool Windows → Profiler или используйте сочетание клавиш Alt+Shift+F10 (Windows/Linux) / ⌥⇧F10 (macOS).
  3. В верхней части окна Profiler вы увидите список активных процессов. Выберите процесс вашего приложения (обычно с именем пакета, например, com.example.myapp).
  4. По умолчанию откроется вкладка CPU. Переключитесь на вкладку Memory (иконка с синим графиком и цифрами).

На графике вы увидите реальное потребление памяти вашим приложением:

  • Java Heap: память, управляемая JVM/ART (ваш код).
  • Native Heap: память, выделенная через NDK (C/C++ код).
  • Graphics: память, используемая для буферов отрисовки.
  • Stack: память под стековые фреймы.
  • Code: память под скомпилированный код.
  • Others: прочее.

Шаг 2: Начните запись и выполните сценарий

  1. На панели инструментов вкладки Memory нажмите кнопку Record memory allocations (иконка с красной точкой внутри кружка). Статус записи изменится на Recording....
  2. Выполните в приложении действие, которое, как вы подозреваете, вызывает утечку. Например:
    • Откройте и закройте фрагмент/активность несколько раз.
    • Прокрутите список (RecyclerView) с большим количеством элементов.
    • Перейдите на другой экран и вернитесь.
  3. Важно: пытайтесь воспроизвести сценарий, который пользователь мог бы выполнить многократно. Утечка часто проявляется после нескольких итераций.
  4. После выполнения действия нажмите кнопку Stop recording (та же кнопка, теперь красная). Запись выделений объектов прекратится.

💡 Совет: Не записывайте слишком долго — это может привести к огромному объёму данных и замедлению работы. 30-60 секунд активных действий обычно достаточно.

Шаг 3: Сделайте Heap Dump для анализа

Запись allocations полезна для отслеживания частоты создания объектов, но для поиска утечек нужен Heap Dump — полный снимок объектов в куче в конкретный момент времени.

  1. Убедитесь, что вы находитесь на вкладке Memory.
  2. Нажмите кнопку Dump Java heap (иконка с кучей мусора и стрелкой вниз). Профилер запросит у системы создание дампа.
  3. Дождитесь завершения. На экране появится новая вкладка Heap Dump с результатами.

Шаг 4: Проанализируйте результаты в Analyzer

Вкладка Heap Dump автоматически открывает анализатор. По умолчанию вы видите таблицу Classes.

Как читать таблицу:

  • Class — имя класса объекта.
  • Instance Count — количество экземпляров этого класса в куче на момент дампа.
  • Size — общий объём памяти, занятый всеми экземплярами этого класса (в КБ/МБ).
  • Retained Sizeсамый важный показатель. Это объём памяти, который будет освобождён, если все объекты этого класса (и те, на которые они ссылаются) будут удалены. Большой Retained Size у класса, который не должен быть в памяти — верный признак утечки.

Первое действие: отсортируйте таблицу по столбцу Retained Size (по убыванию). Вверху окажутся классы, удерживающие наибольшие объёмы памяти.

Что ищем:

  • Классы вашего приложения (например, MyActivity, MyAdapter, MyViewModel), которые не должны существовать после закрытия экрана/фрагмента.
  • Классы из библиотек, известные своими утечками (например, старые версии androidx.lifecycle.ViewModel при неправильной конфигурации).
  • Большое количество экземпляров Bitmap, Context, View, Activity, Fragment.

Шаг 5: Исследуйте пути до GC Root

Нашли подозрительный класс? Теперь нужно понять, почему он не удаляется сборщиком мусора (Garbage Collector). Для этого ищут GC Root — объект, который напрямую или через цепочку ссылок удерживает ваш объект в памяти.

  1. В таблице Classes найдите подозрительный класс и выделите его (кликните на строку).
  2. На панели справа (или в контекстном меню) выберите Show Path to GC Roots.
  3. Появится диалоговое окно с опциями. Выберите Show all paths (или Show soft references, если подозреваете кэши). Нажмите OK.
  4. Анализатор построит дерево ссылок от GC Root (вверху) к вашему объекту (внизу).

Как интерпретировать:

  • GC Root — это обычно системный объект (например, android.app.ActivityThread, java.lang.Thread, android.view.ViewRootImpl), или статическое поле вашего класса (MyClass.sStaticField).
  • Ищите в цепочке контекст (Context) активности, который, например, хранится в статическом поле или долгоживущем синглтоне. Это классическая утечка активности.
  • Если в цепочке есть ваш Singleton или Object из кэша — значит, он не очищается при уничтожении экрана.

⚠️ Важно: Не все пути до GC Root — это утечки. Если объект действительно нужен для работы приложения (например, сервис), его наличие оправдано. Ищите объекты, которые должны были быть уничтожены (закрытые активности, фрагменты).

Проверка результата

После внесения исправлений в код (например, очистки ссылок в onDestroy(), использования WeakReference, замены статических контекстов на ApplicationContext) повторите шаги 1-4.

Критерии успеха:

  1. На графике Memory Profiler после закрытия проблемного экрана память (Java Heap) возвращается к базовому уровню, а не растёт.
  2. В новом Heap Dump количество экземпляров подозрительного класса (Instance Count) значительно сократилось или равно нулю.
  3. В анализаторе Retained Size для этого класса стал близок к нулю.
  4. Путь до GC Root для этого класса больше не существует или ведёт к объекту, который закономерно живёт всё время работы приложения.

Возможные проблемы

1. Profiler не показывает процесс приложения

  • Причина: Приложение собрано в release-конфигурации без debuggable флага, или устройство не авторизовано для отладки.
  • Решение: Запустите debug-сборку. На устройстве в Настройки → Для разработчиков убедитесь, что включена Отладка по USB и вы подтвердили RSA-ключ компьютера.

2. Heap Dump создаётся очень долго или падает с ошибкой

  • Причина: Слишком большое количество объектов в куче (десятки тысяч), нехватка памяти на компьютере для анализа.
  • Решение:
    • Упростите сценарий, делайте дамп раньше.
    • Закройте другие тяжёлые приложения в эмуляторе/устройстве.
    • Увеличьте оперативную память, выделенную эмулятору (в AVD Manager).
    • Используйте фильтры в анализаторе (поле Filter), чтобы смотреть только ваши пакеты (например, com.example).

3. Не могу найти проблемный класс — в списке только системные

  • Причина: Утечка может быть в нативном коде (Native Heap) или в объектах, которые Profiler не может корректно отобразить (например, из-за прозрачных обёрток).
  • Решение:
    • Переключитесь на вкладку Native в Profiler и посмотрите рост native heap.
    • Используйте adb shell dumpsys meminfo <package_name> в терминале для более низкоуровневого отчёта.
    • Если используете библиотеки с нативным кодом (например, OpenGL, FFmpeg), проверьте их документацию на предмет известных утечек.

4. После исправления утечки график памяти всё равно растёт

  • Причина: У вас не одна, а несколько утечек, или рост связан с закономерным кэшированием данных (например, LruCache).
  • Решение:
    • Сравните несколько Heap Dumps, сделанных с интервалом. Посмотрите, какие классы увеличиваются в количестве между дампами.
    • Проверьте, не является ли рост линейным и предсказуемым (например, добавление элементов в ArrayList без очистки). Это может быть не утечкой, а бизнес-логикой.
    • Проанализируйте Allocation Tracker (кнопка Record allocations), чтобы увидеть, где создаются новые объекты.

5. Путь до GC Root ведёт через системный класс, который я не контролирую

  • Причина: Некоторые системные классы (например, InputMethodManager) исторически содержат утечки через неочищаемые ссылки.
  • Решение:
    • Проверьте, не держите ли вы сами ссылку на View или Context в статическом поле или долгоживущем объекте.
    • Для известных системных утечек есть проверенные паттерны: например, в onDestroy() активности вызывать inputMethodManager.isActive = false или очищать фокус у EditText.
    • Поищите конкретный случай (класс + "memory leak") в документации Android или на Stack Overflow.

Часто задаваемые вопросы

Как часто нужно делать Heap Dump для поиска утечек?
Можно ли профилировать память на release-сборке?
Разница между Memory Profiler и Allocation Tracker?
Почему после GC память не опускается до нуля?

Полезное

Запустите Profiler и подключите устройство
Начните запись и выполните сценарий
Сделайте Heap Dump для анализа
Проанализируйте результаты в Analyzer
Исследуйте пути до GC Root