LeakCanary — это библиотека с открытым исходным кодом от Square для автоматического обнаружения утечек памяти в Android-приложениях. Она интегрируется в процесс разработки и в реальном времени отслеживает жизненный цикл Activity, Fragment, ViewModel и других компонентов, сигнализируя об утечках сразу после их возникновения. По данным Square Open Source, библиотека используется в тысячах проектов и считается стандартом де-факто для диагностики памяти на Android.
Главное
LeakCanary — это библиотека для автоматического обнаружения утечек памяти в Android-приложениях, разработанная компанией Square. Она встраивается в процесс сборки приложения и автоматически отслеживает, когда объекты, которые должны быть уничтожены (Activity, Fragment, View), остаются в памяти. При обнаружении утечки LeakCanary формирует heap dump и анализирует цепочку ссылок, удерживающих объект.
Библиотека стала стандартом в Android-сообществе: по данным GitHub, проект собрал более 28 тысяч звёзд и используется в приложениях Google, Uber, Airbnb и Facebook. LeakCanary доступен в двух основных версиях: классическая 1.x (с ручной настройкой) и современная 2.x (автоматическая интеграция через ContentProvider). Версия 2.x не требует изменения Application-класса — зависимости достаточно для полноценной работы.
Основная задача LeakCanary — обнаружить ситуацию, когда объект продолжает существовать в памяти после того, как его жизненный цикл завершён. Это типично для утечек через статические поля, синглтоны, незарегистрированные колбэки, анонимные классы и замыкания, захватывающие внешние объекты.
Утечки памяти на Android критичнее, чем на десктопе, из-за ограниченного объёма RAM на мобильных устройствах. Даже утечка в 5–10 МБ на каждом переходе между экранами может привести к OutOfMemoryError через 30–40 минут использования приложения. LeakCanary обнаруживает такие проблемы на стадии разработки, не дожидаясь падения на проде.
LeakCanary использует слабые ссылки (WeakReference) в комбинации с принудительным вызовом сборщика мусора. Когда Activity или Fragment вызывают onDestroy, LeakCanary создаёт WeakReference на этот объект и запускает GC через короткую задержку (по умолчанию 5 секунд). Если после GC объект всё ещё доступен через WeakReference, значит, он удерживается сильной ссылкой — фиксируется утечка.
После обнаружения утечки LeakCanary делает heap dump (дамп кучи) — полный снимок памяти приложения в формате HPROF. Далее встроенный анализатор (Shark для версии 2.x) строит граф достижимости от GC Roots до утёкшего объекта и находит кратчайший путь — цепочку ссылок, удерживающих объект в памяти.
// Упрощённая логика детекции LeakCanary
class ObjectWatcher {
private val watchedReferences = CopyOnWriteArrayList<KeyedWeakReference>()
fun watch(watchedObject: Any, description: String) {
val reference = KeyedWeakReference(watchedObject, description)
watchedReferences.add(reference)
BackgroundHandler.postDelayed({
checkForLeaks()
}, 5000)
}
private fun checkForLeaks() {
GcTrigger.runGc() // принудительный GC
for (ref in watchedReferences) {
if (ref.get() != null) {
onLeakFound(ref) // объект пережил GC — это утечка
}
}
}
}
Ключевой момент — принудительный вызов GcTrigger.runGc(). Без него невозможно отличить объект, который реально утёк, от объекта, который GC ещё не успел собрать. LeakCanary делает это до трёх раз: если после трёх циклов GC объект всё ещё в памяти — утечка подтверждена.
Shark — это встроенный в LeakCanary 2.x анализатор heap dump, написанный на Kotlin. В отличие от предыдущего анализатора HAHA, Shark не загружает весь HPROF-файл в память, а обходит его граф объектов с минимальными аллокациями. Это снижает потребление RAM при анализе с 50 MB до 2–5 MB и сокращает время анализа с 30 секунд до 1–3 секунд.
Установка LeakCanary 2.x в современный Android-проект занимает одну строку в build.gradle. Библиотека использует ContentProvider для автоматической инициализации — не нужно править Application-класс или добавлять какой-либо код в MainActivity. Подключение выполняется только для debug-сборки, чтобы в релизном APK не было лишнего кода.
// build.gradle (app/module)
dependencies {
// debugImplementation — библиотека только для debug-сборки
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}
После добавления зависимости и пересборки проекта LeakCanary автоматически появляется в приложении. При первом запуске библиотека показывает системное уведомление об активации. Все обнаруженные утечки отображаются в виде уведомлений — нажатие на уведомление открывает экран с детальным отчётом (LeakTrace).
Для кастомизации можно создать собственный AppWatcherInstaller и переопределить параметры: таймаут ожидания GC, список отслеживаемых типов объектов, включение сохранения heap dump на диск. Однако для 90% проектов конфигурация по умолчанию является оптимальной.
Начиная с версии 2.12, LeakCanary поддерживает автоматическое отслеживание ViewModel, корутинных скоупов и State-объектов Compose. Дополнительных зависимостей не требуется — библиотека сама определяет, какие компоненты Jetpack используются в проекте, и активирует соответствующие детекторы.
Отчёт LeakCanary (LeakTrace) — это многострочная цепочка ссылок от GC Root до утёкшего объекта. Каждая строка показывает класс и поле, через которое проходит сильная ссылка. Разработчику нужно читать цепочку снизу вверх: нижняя строка — утёкший объект, верхняя — точка входа (GC Root).
Типичный LeakTrace выглядит так: GC Root → статическое поле Application → синглтон → callback → Activity. Если разработчик видит такую цепочку, проблема ясна: синглтон держит callback, который захватил ссылку на Activity. Решение — заменить сильную ссылку на слабую в синглтоне.
┬
├─ android.app.Application
│ Leaking: NO (Application — singleton)
│ ↓ Application.leakedActivities
├─ java.util.ArrayList
│ Leaking: NO (ArrayList — normal)
│ ↓ ArrayList[0]
├─ com.example.MainActivity
│ Leaking: YES (Activity destroyed but still in memory)
│ ↓ MainActivity.mCallback
├─ com.example.CallbackWrapper
│ Leaking: UNKNOWN
│ ↓ CallbackWrapper.mListener
│ ~~~~~~~~~~
├─ com.example.MyCallback (anonymous)
│ Leaking: UNKNOWN
│ ↓ MyCallback.this$0
├─ com.example.MainActivity
│ Leaking: YES (MainActivity is the leak)
╰
В этом примере LeakCanary показывает, что MainActivity удерживается через цепочку: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → опять MainActivity. Стрелка this$0 указывает, что анонимный класс MyCallback захватил внешнюю ссылку на Activity. Решение — сделать колбэк слабой ссылкой или отменять его в onDestroy.
LeakCanary также показывает статус утечки для каждого элемента цепочки: NO (нет утечки — это корневой элемент), YES (объект должен быть уничтожен), UNKNOWN (не удалось определить статус). Статус UNKNOWN не означает проблемы — это промежуточный объект, который LeakCanary не может однозначно классифицировать.
Переход с версии 1.x на 2.x был кардинальным: разработчики переписали библиотеку с нуля, заменив устаревший HAHA-анализатор на собственный движок Shark, написанный на Kotlin. Shark работает на порядок быстрее, требует меньше памяти для анализа и точнее определяет корневые причины утечек.
| Параметр | LeakCanary 1.x | LeakCanary 2.x |
|---|---|---|
| Язык анализатора | Java (HAHA — fork of Android SDK) | Kotlin (Shark — собственный движок) |
| Установка | Ручная настройка AppWatcher в Application | Автоматическая через ContentProvider |
| Скорость | 10–30 секунд на анализ heap dump | 1–5 секунд на анализ heap dump |
| Производительность | Занимает 10–50 MB RAM при анализе | Занимает 2–10 MB RAM при анализе |
Ключевое преимущество Shark — он не загружает весь heap dump в память, а обходит его граф ссылок с минимальными аллокациями. Это делает LeakCanary 2.x пригодным для использования на устройствах с низким объёмом RAM без риска OutOfMemoryError при анализе.
В версии 2.x также появилась возможность экспорта heap dump в файл для последующего анализа в Android Studio Memory Profiler. Для этого нужно включить настройку dumpHeapWhenLeakFound в конфигурации AppWatcher.
LeakCanary эффективно обнаруживает несколько классов утечек, характерных для Android. Наиболее часто встречается утечка через статические ссылки на Activity — разработчики сохраняют ссылку на контекст Activity в синглтоне, и Activity не может быть собран GC после завершения её жизненного цикла.
Вторая по частоте категория — утечки через незарегистрированные слушатели. Если в onStart был вызван registerListener, но в onStop/onDestroy не вызван unregisterListener — объект-слушатель удерживается системой даже после уничтожения активности. LeakCanary однозначно показывает, какой слушатель и в каком системном сервисе остался живым.
// Типичная утечка: Activity захвачена в callback синглтона
object AnalyticsManager {
private var callback: ((String) -> Unit)? = null
fun register(callback: (String) -> Unit) {
this.callback = callback // сильная ссылка на callback
}
fun unregister() {
callback = null // НЕ ЗАБЫТЬ вызвать в onDestroy!
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AnalyticsManager.register { event ->
logEvent(event) // лямбда захватывает this
}
// если не вызвать unregister в onDestroy → утечка Activity
}
}
Третья категория — утечки через Fragment в BackStack. Если FragmentTransaction.addToBackStack() вызывается без удаления Fragment при возврате, старые экземпляры Fragment остаются в памяти. LeakCanary помогает обнаружить такие скрытые утечки на ранних этапах разработки.
Для каждой обнаруженной утечки LeakCanary предлагает описание и рекомендации по исправлению. В версии 2.14 добавлена интеграция с Android Lint — библиотека может автоматически создавать задачи в issue tracker при обнаружении утечки в CI.
Часто задаваемые вопросы
Да, обязательно. LeakCanary подключается через debugImplementation в build.gradle, что автоматически исключает его из релизной сборки. Если подключить через implementation, библиотека попадёт в релизный APK и будет показывать утечки конечным пользователям — это недопустимо.
Влияние на производительность минимально. LeakCanary активируется только после onDestroy компонента и не вмешивается в отрисовку UI или обработку касаний. Единственная затрата — короткая пауза принудительного GC (около 100 мс) и запись heap dump при утечке (сотые доли секунды).
LeakCanary автоматически сохраняет heap dump в формате HPROF в папку приложения. Файл можно экспортировать через Android Studio: Device File Explorer → data/data/com.example/files/leakcanary/. Для просмотра откройте файл в Memory Profiler через Capture → Open Heap Dump.
Да, начиная с версии 2.12 LeakCanary полностью поддерживает Jetpack Compose. Библиотека отслеживает Composition-контексты и State-объекты, автоматически определяя утечки в Composable-функциях. Отдельная настройка не требуется — работает из коробки.
Ложные срабатывания возможны, но редки. LeakCanary использует тройной вызов GC перед объявлением утечки, что исключает большую часть false positive. Если вы считаете, что срабатывание ложное — создайте IgnoredReference для конкретного класса в конфигурации.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также