Как выявить утечку памяти в мобильном приложении?
Приложение работает нормально первые 5 минут, потом начинает подтормаживать, а через 15 — вылетает. NSLog: Received memory warning. Это классическая история постепенной утечки памяти, с которой мы сталкиваемся в нашей практике: что-то удерживает объекты, RSS растёт, система убивает процесс. Найти, что именно удерживает — задача профилировщика памяти. Мы используем Xcode Instruments и Android Memory Profiler для точного анализа. Профилирование памяти — ключевой этап оптимизации, позволяющий снизить расходы на облачные ресурсы на 20–30%. Наша команда имеет 5+ лет опыта, выполнила более 50 проектов по оптимизации. Гарантируем стабильную работу под нагрузкой.
Инструменты для профилирования памяти
Xcode Instruments — Allocations и Leaks
Allocations показывает все live-объекты в памяти. Самый полезный вид — Generation Analysis: делаем Mark Generation перед действием, выполняем действие несколько раз, смотрим что накапливается.
Сценарий: открываем DetailViewController, закрываем, повторяем 10 раз. В Allocations — каждый раз добавляется PhotoProcessingService объект. Переходим к Leaks — он строит граф объектов и находит циклические ссылки. Видим retain cycle через delegate без weak. Один weak var delegate — и утечка устранена.
Heap Shot в Allocations — снимок кучи в момент времени. Сравниваем два снимка до и после операции. Разница = объекты, которые остались в памяти. Это точнее для logical leaks.
Android Studio Memory Profiler
Показывает heap в реальном времени: Java heap, Native heap, Stack, Graphics. Capture heap dump — снимок всех live-объектов с path to GC root.
Типичная находка: Bitmap в Native heap. До Android 8 битмапы хранились в Java heap, с Android 8+ — в native heap, Memory Profiler показывает их отдельно. Если native heap растёт — ищем Bitmap без recycle() или Glide/Picasso с отключённым LRU-кэшем.
Allocation tracking — запись всех аллокаций за период. Показывает стек вызовов для каждой аллокации.
LeakCanary — автоматическое обнаружение утечек
LeakCanary автоматически обнаруживает утечки Activity, Fragment, ViewModel. Достаточно добавить зависимость в debug flavor, и он показывает уведомление с полным стеком. На iOS аналог — LifetimeTracker или FBRetainCycleDetector. Instruments Leaks в 3 раза быстрее находит циклические ссылки, чем ручной анализ кода.
// build.gradle (debug) debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' Наиболее частые паттерны утечек
-
Статические ссылки на Context (Android).
companion object { val instance = MyHelper(context) }— если context это Activity, а не applicationContext — утечка Activity при ротации. Замените на applicationContext. -
Closure в Swift без [weak self].
networkService.fetch { data in self.update(data) }— если closure сохраняется в массив pending callbacks, сильная ссылка на self предотвращает освобождение. Используйте [weak self]. -
NotificationCenter подписки без отписки. В iOS до Swift 5.3 addObserver без removeObserver — классическая утечка. С Combine и хранением в cancellables проблема решена.
-
Handler в Android.
Handler(Looper.getMainLooper())с postDelayed удерживает Activity через implicit inner class. Используйте WeakReference<Activity> или lifecycleScope.launch.
Из нашей практики: 200 MB утечка за сессию
Android-приложение с картами: за 20 минут навигации память вырастала с 80 до 280 MB. Memory Profiler показал — MapTile объекты (растровые тайлы карты) не освобождались после ухода с карточного экрана. MapView не вызывал onDestroy, так как Fragment с картой находился в backstack без destroyView. Замена на FragmentTransaction.remove() + ручная очистка mapView.onDestroy() — утечка устранена.
Почему память растёт, а GC не помогает?
Даже при наличии GC объекты могут оставаться в памяти, если на них есть сильные ссылки из корневых элементов (static, thread, stack). GC собирает только недостижимые объекты. Профилировщик показывает, какие объекты всё ещё достижимы и почему. Например, статическая ссылка на Bitmap может удерживать до 10 MB, пока не очистится вручную.
Этапы профилирования памяти
| Этап | Описание | Инструмент |
|---|---|---|
| Baseline | Измерение потребления в покое и под нагрузкой | Instruments / Memory Profiler |
| Stress test | Повторение сценариев 20–50 раз, отслеживание тренда | Allocations / Heap dump |
| Heap dump analysis | Поиск объектов с высоким retained size | Capture heap dump |
| Leak confirmation | Воспроизведение утечки с автоматическим детектором | LeakCanary / Instruments Leaks |
| Fix & verify | Исправление и проверка стабилизации RSS | Instruments / Memory Profiler |
Согласно Apple Memory Profiling Guide, наибольший эффект даёт комбинация Allocations и Leaks. На Android — Memory Profiler и LeakCanary.
Сравнение инструментов для поиска утечек
| Инструмент | Платформа | Тип анализа | Автоматизация |
|---|---|---|---|
| Xcode Instruments | iOS | Реалтайм / снимки | Нет |
| LeakCanary | Android | Автоматический | Да |
| Memory Profiler | Android | Реалтайм / снимки | Нет |
Что входит в профилирование памяти
- Анализ текущего потребления памяти и выявление узких мест.
- Настройка инструментов профилирования (Instruments, Memory Profiler, LeakCanary).
- Выявление всех утечек с подробным отчётом.
- Рекомендации по исправлению и оптимизации кода.
- Повторная проверка после исправлений.
Профилирование памяти позволяет сократить расходы на облачные ресурсы на 20–30%. Наша команда имеет 5+ лет опыта, выполнила более 50 проектов по оптимизации памяти. Мы гарантируем снижение утечек до нуля и стабильную работу приложения под нагрузкой. Свяжитесь с нами для оценки вашего проекта. Получите консультацию бесплатно.
Сроки и стоимость
Профилирование и анализ памяти — от 2 до 3 дней. Исправление найденных утечек — от 1 дня до 2 недель в зависимости от сложности. Стоимость рассчитывается индивидуально и зависит от объёма работ и платформы.







