iberck
3/28/2015 - 9:29 PM

Memory leaks

Memory leaks

Garbage collector

Un objeto es candidato a ser elegido por el garbage collector cuando ninguna referencia apunta hacia él.

Memory leak

Un memory leak se da cuando un objeto ya no es utilizado por la aplicación pero no puede ser barrido por el gc porque otro objeto apunta hacia él, esto hace que se quede colgado en memoria.

Tipos de referencias

  • StrongReference: Son las referencias que utilizamos todo el tiempo: Object o = new Object, el objeto será elegible por el gc hasta que ningún objeto apunte a él.
  • SoftReference: Estas referencias serán barridas por el gc cuando se agote la memoria (evite usarlas para cachear objetos en android).
  • WeakReference: Estas referencias serán barridas la siguiente vez que el gc recolecte basura.
  • PhantomReference: the weakest and most enigmatic reference, that get() method will always return null – you can never access the Object it references (even when the Object still exists, and has other references). Realistically, you should probably never be using these.

WeakReference

Se utilizan cuando se desea tener una referencia hacia un objeto, pero esa referencia puede ser barrida en cualquier momento por el garbage collector. Un ejemplo clásico es un caché que desea sea barrido cuando la memoria se infle (a menudo se implementa con WeakHashMap).

Clases anónimas y clases internas

En java las clases internas (no estáticas) y las clases anónimas tienen una referencia implicita hacia su clase externa.

Destrucción de procesos/actividades en android

Android puede destruir actividades y procesos, cuando destruye actividades se destruyen todos sus objetos, por lo que se tienen que regresar a su estado original antes de la destrucción. Cuando destruye el proceso completo, barre con todo incluidos miembros estáticos.

Una actividad es destruida cuando:

  • Cambia su configuración (p.e. se rota la pantalla)
  • El sitema operativo necesita memoria (no hay forma de evitarlo).
  • Se llama el método activity.finish()

Android intenta mantener vivos los procesos de las aplicaciones, sin embargo en ocasiones el proceso completo de la aplicación puede ser destruido cuando android requiere memoria, a consecuencia de esto las variables estáticas pueden morir.

Las actividades y vistas tienen métodos para recuperar su estado y restaurarlo en una nueva instancia.

Cuando un proceso termina, android intenta volver a la misma actividad en donde se quedó antes de matar el proceso.

Todas las vistas tienen una referencia hacia la actividad donde son creadas. Por lo tanto, cuando muere la actividad también mueren todas las instancias de los componentes gráficos asociados a ella (vistas).

Destruir procesos manualmente

Para reproducir matar el proceso como lo haría android: Bajar la aplicación con el botón home, matar el proceso, dejar presionado home y elegir el recuadro de la app.

Referencia: Testing android resets

¿Qué sucede si una actividad tiene un memory-leak?

Cuando se rota la pantalla android destruye la instancia de la actividad (sin importar si tiene o no un memory-leak) y crea otra para ser utilizada. La consecuencia de que la actividad tenga un leak es que no podrá ser barrida la referencia por el gc y quedará colgada en memoria.

Ejemplos memory leaks

Clase anónima

public void onResume() {
    super.onResume();
 
    SomeObject object = new SomeObject();
 
    // obj->eventListener->actividad(implicitamente)
    object.setSuccessListener(new EventListener<Boolean>() {
        public void onEvent(Boolean response) {
            Log.d(TAG_NAME, "Valid response? "+response);
        }
    });
 
    // MemoryLeak: aquí sin darnos cuenta se ata la actividad hacia el singleton
    // que vive durante todo el ciclo de vida de la app y no permite al gc
    // barrer la actividad (el singleton apunta hacia ella).
    SomeObjectManager.getSingleton().addObject(object);
}

Consejos para evitar memory leaks.

  • No asociar referencias de objetos de la actividad hacia miembros globales. No asociar el contexto o cualquiera de sus vistas hacia un lugar externo de larga duración, por ejemplo un singleton.
  • Preferir clases estáticas en vez de internas o anónimas (tienen una referencia implícita hacia this, es decir hacia la actividad).
  • Utilizar WeakReference.
  • Soltar objetos (comunmente listeners) en los ciclos de vida: onStop(),onDestroy().
  • getLastNonConfigurationInstance: Hay que tener cuidado de no retornar objetos que tengan agarrado el contexto porque la consecuencia es un memory leak.

Nota: Cuando un thread toma una actividad y no la suelta, se está creando un memory-leak sin embargo el thread soltará la actividad cuando termine su ejecucución y ésta podrá ser elegible y barrida por el gc.

Nota: Una variable estática no provoca memory leaks mientras no tome el contexto o alguno de sus objetos y no lo suelte.

Recursos

activitys-threads-memory-leaks