Introducción a la gestión de la memoria y a las fugas de memoria en Android

Hay que tener en cuenta que aunque esta herramienta puede ser muy útil para encontrar referencias a una supuesta fuga, puede ser bastante difícil y compleja de usar para encontrar fugas desconocidas y requiere un poco de análisis. Dicho esto, si la dominas, es una gran herramienta para tener en tu haber.

LeakCanary

Imagen de LeakCanary

Otra forma de encontrar fugas de memoria es mediante el uso de Leak Canary, una biblioteca creada por Square que te ayuda a detectar fugas de memoria. Funciona mediante el uso de una clase ObjectWatcher que mantiene referencias débiles a objetos destruidos en el montón. Si la referencia no se borra en los siguientes cinco segundos y el Garbage Collector se ha ejecutado, el objeto se considera retenido y registra esto como una potencial fuga de memoria en el logcat. Bastante inteligente, ¿no?

Para empezar, simplemente añade la librería dentro de tus dependencias a tu archivo build.gradle, sincroniza y compila.

Agrega la librería LeakCanary

Tan pronto como inicies la aplicación, podrás ver la salida de LeakCanary en el logcat mientras observa los instantes y retiene los objetos. Para desencadenar una fuga, añadí un par de nuevas tareas en mi aplicación TaskManager. No mucho después, recibí esta notificación de LeakCanary que indica una posible fuga!

Sólo hay que hacer clic en la notificación para «volcar la pila» para investigar la posible fuga y se mostrará este diálogo en la aplicación.

LeakCanary está investigando las fugas ..

A continuación, Leak Canary analiza el montón mediante la localización de los objetos retenidos y encuentra el rastro de la fuga que es una ruta de referencias a cada objeto retenido. Una vez hecho esto, se mostrará una notificación que resume el número de objetos retenidos y las fugas.

¡Parece que ha encontrado nuestra fuga! Si pulsamos sobre ella veremos el rastro completo de la fuga:

Nos dice que TaskActivity tiene una fuga y las referencias subrayadas muestran el rastro, se está filtrando desde mContext en SingletonExample – ¡suena familiar! Ahora ya sabemos cómo encontrar una fuga usando LeakCanary. 😃

Evitando las fugas de memoria

Así que ahora que hemos encontrado nuestra fuga de memoria, ¿cómo podemos evitarlas en el futuro? A continuación, algunas de las causas y patrones más comunes.

Foto de Bogomil Mihaylov en Unsplash

Receptores de difusión

Los receptores de difusión se pueden utilizar para escuchar los eventos de difusión de todo el sistema o los intentos que indican información del dispositivo como la batería baja, la fecha y los cambios de conectividad, por ejemplo, que se ha desactivado el modo avión. Al utilizarlos, hay que acordarse de anular el registro de los receptores de difusión, ya que, de lo contrario, se mantendrá inevitablemente una referencia a la actividad.

Cómo evitarlo: Todo lo que hay que hacer es llamar a unregister() en su receptor broadcast en onStop() en su actividad.

Este patrón también se encuentra para asyncTask, TimerTask e hilos que necesitan ser cancelados en onDestroy() para evitar una fuga.

Contexto a clase Singleton

A veces necesitamos pasar contexto de una actividad a una clase Singleton. Un ejemplo de esto sería una clase utils donde necesitamos acceder a recursos, servicios o archivos internos. Sin embargo, pasar contexto significa que inevitablemente mantenemos una referencia a la actividad.

Cómo evitarlo: En lugar de pasar this de una actividad, podemos pasar el contexto de la aplicación si está disponible (si quieres saber más sobre cuándo usar qué contexto, ¡este artículo me ha resultado muy útil!) Una solución alternativa es asegurar que el contexto Singleton sea null dentro del método de la actividad onDestroy().

Referencias estáticas

Referenciar una vista o una actividad como estática significa que la referencia a la actividad no será recolectada por la basura. Esto simplemente debe evitarse en todo momento.

Cómo evitarlo: Si por alguna razón tienes que hacerlo, puedes asegurarte de que se destruya poniéndola a null en onDestroy().

Referencias a clases internas

Las clases internas suelen provocar fugas al mantener una referencia implícita a la clase externa. Esto sucede la variable de clase se declara como estática o si la propia clase no se declara como estática. ¿Confuso? Sí, pero es fácil de evitar si seguimos la sencilla regla que sigue.

Cómo evitarlo: Haz que la clase interna sea estática para evitar mantener una referencia a la clase externa y nunca crees una variable estática de una clase interna. Lo mismo se aplica a las clases anónimas.

¡Eso es todo! Espero que hayas aprendido algunas cosas sobre las fugas de memoria y cómo evitarlas. ¡Feliz codificación! 😄

Aquí tienes una serie de artículos y documentación que me resultaron especialmente útiles para aprender sobre las fugas de memoria:

Deja una respuesta

Tu dirección de correo electrónico no será publicada.