Fugas de memoria e hinchazón — qué es, causas y cómo evitarlo

Autor: IT Sectr Publicado: 2026-07-28 Tiempo de lectura: 10 min

Fuga de memoria — uno de los problemas más insidiosos en el desarrollo móvil. El uso de memoria de la aplicación crece constantemente hasta alcanzar el límite establecido por el SO, seguido de un OutOfMemoryError o una terminación forzada. Según Square Engineering, alrededor del 40% de las aplicaciones Android tienen al menos una fuga de memoria que solo puede detectarse mediante perfilado. Analicemos las causas y los métodos para prevenir el crecimiento de la memoria.

Puntos Clave

  • GC reachability — un objeto no se elimina si hay una referencia activa desde el conjunto raíz
  • Referencias estáticas a Activity o Context — la causa más común de fugas en Android
  • LeakCanary — la herramienta estándar para la detección automática de fugas en Android
  • WeakReference — una solución para referencias que no deben impedir la recolección de basura
  • Componentes Lifecycle-aware cancelan automáticamente las suscripciones al destruirse la vista

¿Qué es una fuga de memoria y la hinchazón de la aplicación?

Fuga de memoria — una situación en la que un objeto que ya no necesita la aplicación sigue siendo retenido en el heap porque una referencia activa desde el conjunto raíz (GC Root) aún apunta a él. El recolector de basura considera que dicho objeto está vivo y no lo elimina.

Hinchazón de memoria — un problema más amplio en el que la aplicación consume más memoria de la necesaria para realizar sus tareas actuales. Causas: almacenamiento en caché excesivo, duplicación de objetos, estructuras de datos subóptimas y fragmentación del heap.

En Android, cada aplicación tiene un heap limitado (generalmente 64–512 MB según el dispositivo y la versión del SO). En iOS, el límite es menos estricto, pero el sistema envía una advertencia de memoria al acercarse al umbral.

CaracterísticaAndroidiOS
Límite del heap64–512 MB (depende del dispositivo)Implícito (sistema)
Recolección de basuraART (Concurrent, Compact)ARC (Automatic Reference Counting)
Mecanismo de fugaReferencias GC RootRetain cycles (ciclos de referencia fuertes)
ResultadoOutOfMemoryErrorAdvertencia de memoria → terminación

Según el Facebook Engineering Blog, las fugas de memoria causan ~15% de los informes de crash en aplicaciones móviles. En Android, esto se ve agravado por los ANR debidos a las frecuentes pausas del GC cuando la memoria es escasa.

Patrones comunes de fugas de memoria en Android e iOS

Referencia estática a Activity — una fuga clásica de Android. Si un campo estático o singleton mantiene una referencia a una Activity, esta no será recolectada por el GC incluso después de finish() mientras el singleton esté vivo. Una Activity es un objeto pesado que contiene una jerarquía de vistas, recursos y Context.

kotlin
object LeakHolder {
    var activityRef: Activity ?= null // leak: static reference to Activity
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle ?= null) {
        super.onCreate(savedInstanceState)
        LeakHolder.activityRef = this // ❌ MainActivity will never be GC'd
    }
}

Clases anónimas y lambdas — retienen implícitamente una referencia a la clase externa. Si un Runnable o Callback se pasa a un servicio externo y la Activity se destruye, el objeto de la clase anónima aún permanece en la cola e impide que la Activity sea recolectada.

  • Handler con retardo — si la Activity se destruye pero Handler.postDelayed aún no se ha ejecutado, la Activity se fuga
  • Thread y AsyncTask — al rotar la pantalla, la Activity se recrea mientras el Thread antiguo sigue manteniendo una referencia a la Activity anterior
  • Retrofit/Callback — un Callback anónimo mantiene una referencia al presenter o fragment
  • Observadores — suscripciones de LiveData o RxJava sin cancelación en onDestroy

En iOS, el principal problema son los retain cycles: dos objetos mantienen referencias fuertes entre sí y ARC no puede poner a cero el contador de referencias de ninguno. Un caso típico: un closure que captura self fuertemente y self que mantiene una referencia al closure.

¿Cómo detectar fugas de memoria?

LeakCanary — una biblioteca de Square para la detección automática de fugas en Android. Tras destruir una Activity o Fragment, comprueba si el objeto fue recolectado por el GC. Si no, hace un heap dump y muestra el trace de la fuga.

kotlin
// LeakCanary 2.x — auto-integration via Application
class ExampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        // LeakCanary auto-installs in debug build
        // via ContentProvider — zero code setup
    }
}

// Force check invocation
AppWatcher.objectWatcher.watch(watchedObject, "leak description")

Android Studio Profiler — una herramienta integrada para la monitorización de memoria en tiempo real. Permite grabar un heap dump, encontrar objetos sospechosos (Retained Size > 1 MB) y rastrear la ruta GC root hasta cada objeto.

Para iOS, usa el Xcode Memory Graph Debugger. Visualiza el grafo de objetos en memoria, muestra retain cycles y permite detectar instantáneamente referencias circulares. Instruments > Allocations también está disponible para monitorización a largo plazo.

Estrategias de prevención

WeakReference — un mecanismo básico para referencias que no deben interferir con la recolección de basura. Si el GC decide recolectar un objeto, WeakReference devuelve null. Se usa para callbacks, listeners y referencias a componentes de UI desde hilos en segundo plano.

Componentes Lifecycle-aware — un enfoque arquitectónico implementado en Android Jetpack (Lifecycle, LiveData, Flow, coroutines). Las suscripciones se cancelan automáticamente en onDestroy, eliminando la principal clase de fugas.

kotlin
class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<List<User>>()
    val data: LiveData<List<User>> get() = _data

    fun loadData() {
        viewModelScope.launch {
            val result = repository.fetchData()
            _data.postValue(result)
            // coroutine auto-cancels on onCleared()
        }
    }
}

viewModelScope y lifecycleScope — CoroutineScope integrados en Android que se cancelan en el evento de ciclo de vida correspondiente. Esto elimina las fugas a través de corutinas — el escenario más común en el desarrollo moderno de Android.

  • No uses referencias estáticas a Context, Activity, View o Fragment
  • Cancelas todas las suscripciones de RxJava en disposeBag / CompositeDisposable en onDestroy
  • Usa [weak self] / [unowned self] en closures de iOS para prevenir retain cycles
  • Verifica Bitmap y objetos grandes — deben ser reciclados o anulados

Herramientas de perfilado de memoria

Memory Profiler in Android Studio — la herramienta principal para la monitorización del heap. Muestra asignaciones en vivo, instantáneas del heap y recuentos de objetos por tipo. Permite grabar un volcado y analizarlo en MAT (Memory Analyzer Tool) para encontrar objetos sospechosos.

Eclipse MAT — un analizador de heap dump de escritorio. Tras cargar un archivo HPROF de Android Studio, MAT construye un árbol dominador, muestra el retain size de cada objeto y ofrece un análisis automático de fugas sospechosas mediante el Leak Suspects Report.

Xcode Memory Graph — un depurador visual de retain cycles. Al hacer clic en el botón Memory Graph Debugger, Xcode detiene la aplicación, construye un grafo completo de objetos en memoria y resalta los retain cycles en rojo.

HerramientaPlataformaCaracterística
LeakCanaryAndroidDetección automática de fugas tras destroy
Memory ProfilerAndroid StudioHeap dump + asignaciones en vivo
Eclipse MATAndroidÁrbol dominador, Leak Suspects Report
Memory GraphiOS (Xcode)Visualizador de retain cycles

Según Google I/O 2023, las aplicaciones que usan LeakCanary en builds de depuración reducen los crashes relacionados con la memoria en un 30–50% en los primeros 2 meses tras su adopción. Se recomienda añadir LeakCanary durante la fase de inicio del proyecto.

Preguntas Frecuentes

¿En qué se diferencia una fuga de memoria de la hinchazón?

Fuga — objetos que no son accesibles por el código pero no son recolectados por el GC debido a referencias activas. Hinchazón — la aplicación mantiene objetos que son lógicamente necesarios pero en cantidad excesiva (por ejemplo, un caché de 50 MB en una aplicación en ejecución de 80 MB). La hinchazón se soluciona arquitectónicamente; la fuga, mediante una gestión correcta de referencias.

¿Cómo encuentra LeakCanary las fugas?

LeakCanary usa ObjectWatcher — tras onDestroy() de una Activity, crea una WeakReference a la Activity y ejecuta el GC. Si la WeakReference no se limpia tras 5 segundos, LeakCanary hace un heap dump, analiza la cadena de referencia más corta desde el GC Root hasta el objeto y muestra el stack exacto de la fuga con el archivo y la línea de código.

¿Por qué Bitmap causa a menudo OutOfMemoryError?

Bitmap ocupa memoria fuera del heap de Java en la memoria nativa (native heap). El tamaño de un Bitmap = ancho × alto × 4 bytes (ARGB_8888). Una foto de 12 MP (4000×3000) ocupa 48 MB. Android no siempre puede liberar la memoria nativa a tiempo, por lo que la acumulación de varios Bitmaps provoca OOM incluso con suficiente heap de Java.

¿Qué es un retain cycle en iOS?

Retain cycle — una situación en ARC donde dos objetos mantienen referencias fuertes entre sí y el contador de referencias nunca llega a cero. Un ejemplo típico: un ViewController con una referencia fuerte a un closure, y el closure captura self fuertemente. Solución: usa [weak self] o [unowned self] en los closures.

¿Cuál es el tamaño máximo del heap en Android?

El tamaño del heap depende del dispositivo y la versión de Android. Para dispositivos antiguos (API 15–24) — 64–128 MB. Para los modernos (API 25+) — 256–512 MB. El valor exacto se puede obtener mediante ActivityManager.getMemoryClass(). Para aplicaciones grandes (juegos, editores), largeHeap=true en el manifiesto proporciona hasta 1 GB.

Resumen

  • Fuga de memoria — un objeto no recolectado por el GC debido a una referencia activa desde el conjunto raíz; hinchazón — consumo excesivo de memoria sin fugas explícitas
  • Referencias estáticas a Activity, Context o View — la causa número uno de fugas en Android; solución — WeakReference o Application Context
  • Clases anónimas y lambdas retienen implícitamente una referencia a la clase externa; los callbacks no cancelados son la segunda causa más común
  • LeakCanary — el estándar para la detección automática de fugas en Android; la integración toma 5 minutos y reduce la tasa de crashes en un 30–50%
  • lifecycleScope y viewModelScope cancelan automáticamente las corutinas al destruirse, eliminando toda una clase de fugas
  • Retain cycles en iOS se solucionan con weak/unowned self en closures y delegados
  • Perfila la memoria al menos una vez por sprint — un heap dump con MAT o Memory Graph debería formar parte del code review

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.

Discutir el proyecto

Lea también