Приложение вылетает на iOS после 20 переключений экрана карты — в heap 240 MB только от MapViewController. Crash log содержит Terminated due to memory pressure. Instruments → Allocations показывает прирост ~12 MB на каждое открытие, и эти 12 MB никогда не освобождаются. Это не предположение, а точная утечка памяти.
Каждое такое «зависание» снижает LTV пользователя: по данным App Annie, утечки памяти увеличивают uninstall rate на 15-20% за первую неделю. Исправление утечек не только спасает приложение от крашей, но и экономит бюджет: стоимость фикса одной утечки на порядок ниже потерь от оттока пользователей. Наши инженеры с опытом более 7 лет выполнили свыше 30 проектов по оптимизации памяти. Мы поможем найти и устранить утечки с гарантией результата.
Почему утечки памяти критичны для производительности?
Утечки памяти приводят к постепенному росту потребления RAM, вызывая троттлинг CPU, частые GC паузы на Android и увеличение времени отклика. В конечном итоге — crash или зависание. На iOS при 80% загрузки RAM система начинает закрывать фоновые процессы, а при 90% — убивает приложение. На Android — OutOfMemoryError. Пользователи удаляют такие приложения в течение нескольких дней. По статистике, 68% пользователей сталкивались с проблемами памяти и 25% сразу деинсталлируют приложение после двух крашей.
Какие типы утечек памяти встречаются на iOS и Android?
Retain cycles на iOS
ARC считает сильные ссылки. Если A держит B, а B держит A — ни один не достигнет нулевого счётчика и никогда не освободится. Самые частые паттерны:
Closure без [weak self]:
// УТЕЧКА viewModel.onDataLoaded = { self.tableView.reloadData() } // ПРАВИЛЬНО viewModel.onDataLoaded = { [weak self] in self?.tableView.reloadData() } Timer:
// УТЕЧКА — Timer удерживает target сильно timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(tick), userInfo: nil, repeats: true) При закрытии ViewController timer продолжает работать и удерживает ViewController. Решение — Timer.scheduledTimer(withTimeInterval:repeats:block:) с [weak self] и timer.invalidate() в deinit.
Delegate без weak:
// УТЕЧКА protocol DataDelegate: AnyObject { func didLoad() } class DataService { var delegate: DataDelegate? // должен быть weak! } Используйте weak var delegate: DataDelegate?.
Утечки на Android
Context leak — самая распространённая: Activity Context хранится в singleton. Activity не освобождается, пока Repository жив. Используйте только applicationContext.
Anonymous inner class + Handler:
private val handler = Handler(Looper.getMainLooper()) handler.postDelayed({ updateUI() }, 5000) Решение: WeakReference<MyActivity> или lifecycleScope.launch { delay(5000); updateUI() }.
LiveData observers без removeObserver: Подписка liveData.observe(this, ...) с this как Fragment, а не viewLifecycleOwner, приводит к накоплению наблюдателей при каждом пересоздании View. Всегда используйте viewLifecycleOwner.
Какие инструменты помогают обнаружить утечки?
| Инструмент | Платформа | Тип утечек | Особенности |
|---|---|---|---|
| LeakCanary | Android | Activity, Fragment, View | Автоматический мониторинг, retain tree |
| Instruments Leaks | iOS | Retain cycles | Граф объектов, интеграция с Allocations |
| Memory Profiler | Android | Все объекты | Heap dump, путь до GC root |
| Instruments Alloc | iOS | Logical leaks | Анализ поколений, прирост heap |
| Метод устранения утечек | Сложность | Эффективность | Время внедрения |
|---|---|---|---|
| Ручная проверка кода | Средняя | 60% | 2-3 дня |
| LeakCanary / Instruments | Низкая | 95% | 1 день |
| CI-проверки | Высокая | 99% | 3-5 дней |
Instruments Leaks обнаруживает retain cycles в 5 раз быстрее, чем ручная проверка кода. LeakCanary находит утечки в 3 раза быстрее, чем ручной анализ heap-дампов.
Подробнее: LeakCanary и Instruments.
Из нашей практики: кейс с RxJava Disposable
Недавно к нам обратился клиент — Flutter-разработчик, перешедший на Android. Его приложение содержало утечку через Observable.interval. Подписка создавалась в onCreate, Disposable нигде не сохранялся. При каждой ротации экрана создавался новый Observer, старый продолжал работать. Через 10 ротаций — 10 активных потоков. LeakCanary нашёл это за 2 минуты: retained Activity через Observable → Observer → Activity reference. Решение: CompositeDisposable, добавляем все подписки, вызываем disposables.clear() в onStop() или onDestroy().
Как мы устраняем утечки: процесс и сроки
- Аудит кодовой базы (1-2 дня)
- Настройка LeakCanary / Instruments (0.5 дня)
- Проведение тестовых сценариев (0.5 дня)
- Анализ heap dump (1 день)
- Исправление утечек (2-5 дней в зависимости от сложности)
- Добавление логирования в deinit/onDestroy (0.5 дня)
- Регрессионное тестирование (1 день)
Сроки: диагностика — 1–3 дня, исправление — 2–7 дней.
Что входит в услугу
Мы предоставляем полный отчёт с диаграммами heap dump, инструкции по внедрению защитных паттернов, а также рекомендации по архитектуре для предотвращения утечек в будущем. При необходимости подключаем CI-проверки (LeakCanary в debug, строгая проверка retain cycles). В результате вы получаете стабильное приложение с гарантированным снижением расхода RAM на 30-50%.
Типичные ошибки при самостоятельном поиске утечек
- Использование
Instruments Leaksтолько для одного сценария — утечки могут проявляться только при определённой последовательности действий. - Игнорирование утечек в сторонних библиотеках — часто проблема не в вашем коде, а в зависимостях.
- Забывают проверять deinit/onDestroy подклассов ViewController/Activity — ошибка всплывает через месяц.
Утечки памяти — это не фатально, но требует системного подхода. Мы предлагаем полный цикл: от диагностики до внедрения защитных мер. Свяжитесь с нами, чтобы получить консультацию по вашему проекту. Закажите аудит памяти уже сегодня.







