LeakCanary es una biblioteca de código abierto de Square para la detección automática de fugas de memoria en aplicaciones Android. Se integra en el proceso de desarrollo y monitorea en tiempo real el ciclo de vida de Activity, Fragment, ViewModel y otros componentes, señalando las fugas tan pronto como ocurren. Según Square Open Source, la biblioteca se utiliza en miles de proyectos y se considera el estándar de facto para el diagnóstico de memoria en Android.
Puntos clave
LeakCanary es una biblioteca para la detección automática de fugas de memoria en aplicaciones Android, desarrollada por Square. Se integra en el proceso de compilación de la aplicación y monitorea automáticamente si los objetos que deberían destruirse (Activity, Fragment, View) permanecen en memoria. Al detectar una fuga, LeakCanary genera un heap dump y analiza la cadena de referencias que retiene el objeto.
La biblioteca se ha convertido en un estándar en la comunidad Android: según GitHub, el proyecto cuenta con más de 28 mil estrellas y se utiliza en aplicaciones de Google, Uber, Airbnb y Facebook. LeakCanary está disponible en dos versiones principales: la clásica 1.x (con configuración manual) y la moderna 2.x (integración automática mediante ContentProvider). La versión 2.x no requiere modificar la clase Application — la dependencia es suficiente para un funcionamiento completo.
La tarea principal de LeakCanary es detectar cuándo un objeto continúa existiendo en memoria después de que su ciclo de vida ha terminado. Esto es típico de fugas a través de campos estáticos, singletons, callbacks no registrados, clases anónimas y cierres que capturan objetos externos.
Las fugas de memoria en Android son más críticas que en escritorio debido a la RAM limitada en dispositivos móviles. Incluso una fuga de 5–10 MB en cada transición de pantalla puede provocar un OutOfMemoryError después de 30–40 minutos de uso de la aplicación. LeakCanary detecta estos problemas en la etapa de desarrollo, sin esperar a un fallo en producción.
LeakCanary utiliza referencias débiles (WeakReference) combinadas con la recolección de basura forzada. Cuando una Activity o Fragment llama a onDestroy, LeakCanary crea una WeakReference a ese objeto y ejecuta el GC tras una breve pausa (5 segundos por defecto). Si el objeto sigue siendo accesible a través de WeakReference después del GC, significa que está retenido por una referencia fuerte — se registra una fuga.
Tras detectar una fuga, LeakCanary realiza un heap dump (volcado de memoria) — una instantánea completa de la memoria de la aplicación en formato HPROF. A continuación, el analizador integrado (Shark para la versión 2.x) construye un gráfico de accesibilidad desde las GC Roots hasta el objeto filtrado y encuentra la ruta más corta — la cadena de referencias que mantiene el objeto en memoria.
// Lógica simplificada de detección de 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 forzado
for (ref in watchedReferences) {
if (ref.get() != null) {
onLeakFound(ref) // el objeto sobrevivió al GC — es una fuga
}
}
}
}
El punto clave es la llamada forzada a GcTrigger.runGc(). Sin ella, es imposible distinguir un objeto que realmente ha fugado de uno que el GC aún no ha recolectado. LeakCanary lo hace hasta tres veces: si después de tres ciclos de GC el objeto sigue en memoria, la fuga se confirma.
Shark es el analizador de heap dump integrado en LeakCanary 2.x, escrito en Kotlin. A diferencia del analizador anterior HAHA, Shark no carga todo el archivo HPROF en memoria, sino que recorre su gráfico de objetos con asignaciones mínimas. Esto reduce el consumo de RAM durante el análisis de 50 MB a 2–5 MB y acorta el tiempo de análisis de 30 segundos a 1–3 segundos.
Instalar LeakCanary 2.x en un proyecto Android moderno requiere una sola línea en build.gradle. La biblioteca utiliza ContentProvider para la inicialización automática — no es necesario modificar la clase Application ni añadir código a MainActivity. La dependencia se añade solo para compilaciones debug, para que los APK de lanzamiento no contengan código extra.
// build.gradle (app/module)
dependencies {
// debugImplementation — biblioteca solo para compilaciones debug
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}
Después de añadir la dependencia y reconstruir el proyecto, LeakCanary aparece automáticamente en la aplicación. En el primer inicio, la biblioteca muestra una notificación del sistema confirmando la activación. Todas las fugas detectadas aparecen como notificaciones — al tocar una notificación se abre una pantalla con un informe detallado (LeakTrace).
Para personalizar, puedes crear tu propio AppWatcherInstaller y sobrescribir parámetros: tiempo de espera del GC, lista de tipos de objetos rastreados, activación del guardado de heap dump en disco. Sin embargo, para el 90% de los proyectos, la configuración por defecto es óptima.
A partir de la versión 2.12, LeakCanary admite el rastreo automático de ViewModel, ámbitos de corrutinas y objetos State de Compose. No se necesitan dependencias adicionales — la biblioteca detecta automáticamente qué componentes de Jetpack se utilizan en el proyecto y activa los detectores correspondientes.
Un informe de LeakCanary (LeakTrace) es una cadena de referencias de varias líneas desde la GC Root hasta el objeto filtrado. Cada línea muestra la clase y el campo a través del cual pasa una referencia fuerte. El desarrollador debe leer la cadena de abajo arriba: la línea inferior es el objeto filtrado, la línea superior es el punto de entrada (GC Root).
Un LeakTrace típico tiene este aspecto: GC Root → campo estático de Application → singleton → callback → Activity. Si un desarrollador ve esta cadena, el problema está claro: el singleton mantiene un callback que capturó una referencia a la Activity. La solución es sustituir la referencia fuerte por una débil en el singleton.
┬
├─ 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)
╰
En este ejemplo, LeakCanary muestra que MainActivity se retiene a través de la cadena: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → MainActivity de nuevo. La flecha this$0 indica que la clase anónima MyCallback capturó una referencia externa a la Activity. La solución es hacer que el callback sea una referencia débil o cancelarlo en onDestroy.
LeakCanary también muestra el estado de fuga para cada elemento de la cadena: NO (sin fuga — es un elemento raíz), YES (el objeto debe destruirse), UNKNOWN (no se pudo determinar el estado). El estado UNKNOWN no significa un problema — es un objeto intermedio que LeakCanary no puede clasificar de forma concluyente.
La transición de la versión 1.x a la 2.x fue radical: los desarrolladores reescribieron la biblioteca desde cero, sustituyendo el obsoleto analizador HAHA por su propio motor Shark, escrito en Kotlin. Shark es un orden de magnitud más rápido, requiere menos memoria para el análisis y determina con mayor precisión las causas raíz de las fugas.
| Parámetro | LeakCanary 1.x | LeakCanary 2.x |
|---|---|---|
| Lenguaje del analizador | Java (HAHA — fork de Android SDK) | Kotlin (Shark — motor propio) |
| Instalación | Configuración manual de AppWatcher en Application | Automática mediante ContentProvider |
| Velocidad | 10–30 segundos para análisis de heap dump | 1–5 segundos para análisis de heap dump |
| Rendimiento | Ocupa 10–50 MB de RAM durante el análisis | Ocupa 2–10 MB de RAM durante el análisis |
La ventaja clave de Shark es que no carga todo el heap dump en memoria, sino que recorre su gráfico de referencias con asignaciones mínimas. Esto hace que LeakCanary 2.x sea adecuado para su uso en dispositivos con poca RAM sin riesgo de OutOfMemoryError durante el análisis.
La versión 2.x también introdujo la capacidad de exportar heap dumps a un archivo para su posterior análisis en Android Studio Memory Profiler. Para ello, activa la opción dumpHeapWhenLeakFound en la configuración de AppWatcher.
LeakCanary detecta eficazmente varias clases de fugas comunes en Android. La más frecuente es la fuga a través de referencias estáticas a una Activity — los desarrolladores mantienen una referencia al contexto de Activity en un singleton, y la Activity no puede ser recolectada por el GC después de que su ciclo de vida termina.
La segunda categoría más común son las fugas a través de oyentes no registrados. Si se llamó a registerListener en onStart pero no se llamó a unregisterListener en onStop/onDestroy, el objeto oyente es retenido por el sistema incluso después de que la actividad se destruye. LeakCanary muestra claramente qué oyente y en qué servicio del sistema permanece vivo.
// Fuga típica: Activity capturada en un callback de singleton
object AnalyticsManager {
private var callback: ((String) -> Unit)? = null
fun register(callback: (String) -> Unit) {
this.callback = callback // referencia fuerte al callback
}
fun unregister() {
callback = null // NO OLVIDES llamar en onDestroy!
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AnalyticsManager.register { event ->
logEvent(event) // la lambda captura this
}
// si no se llama a unregister en onDestroy → fuga de Activity
}
}
La tercera categoría son las fugas a través de Fragment en BackStack. Si se llama a FragmentTransaction.addToBackStack() sin eliminar el Fragment al retroceder, las instancias antiguas de Fragment permanecen en memoria. LeakCanary ayuda a detectar estas fugas ocultas en las primeras etapas del desarrollo.
Para cada fuga detectada, LeakCanary proporciona una descripción y recomendaciones para solucionarla. La versión 2.14 añadió integración con Android Lint — la biblioteca puede crear automáticamente tareas en el sistema de seguimiento de incidencias cuando se detecta una fuga en CI.
Preguntas frecuentes
Sí, absolutamente. LeakCanary se añade mediante debugImplementation en build.gradle, lo que lo excluye automáticamente de las compilaciones de lanzamiento. Si se usa implementation, la biblioteca se incluirá en el APK de lanzamiento y mostrará fugas a los usuarios finales — esto es inaceptable.
El impacto en el rendimiento es mínimo. LeakCanary solo se activa después del onDestroy de un componente y no interfiere con el renderizado de la interfaz ni el manejo de toques. El único costo es una breve pausa forzada del GC (unos 100 ms) y la escritura del heap dump cuando ocurre una fuga (fracciones de segundo).
LeakCanary guarda automáticamente los heap dumps en formato HPROF en la carpeta de la aplicación. El archivo se puede exportar mediante Android Studio: Device File Explorer → data/data/com.example/files/leakcanary/. Para verlo, abre el archivo en Memory Profiler a través de Capture → Open Heap Dump.
Sí, a partir de la versión 2.12 LeakCanary es totalmente compatible con Jetpack Compose. La biblioteca rastrea los contextos de Composition y los objetos State, detectando automáticamente fugas en funciones Composable. No se necesita configuración adicional — funciona directamente.
Los falsos positivos son posibles pero raros. LeakCanary utiliza una triple llamada al GC antes de declarar una fuga, lo que elimina la mayoría de los falsos positivos. Si crees que una detección es un falso positivo, crea un IgnoredReference para la clase específica en la configuración.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también