La gestión automática de memoria mediante la recolección de basura es un mecanismo clave de la plataforma Android, basado en la máquina virtual ART. Según Google Android Documentation, 2026, el recolector de basura libera al desarrollador de la gestión manual de memoria, eliminando automáticamente los objetos que ya no tienen referencias. Sin GC, cada asignación de objeto requeriría una llamada explícita a free o delete, lo que en el ecosistema Java con millones de objetos por segundo es físicamente imposible.
Puntos clave
Garbage Collection (GC) es un proceso automático de detección y liberación de memoria ocupada por objetos que ya no son utilizados por el programa. En el contexto del desarrollo móvil, GC se utiliza en la plataforma Android a través de la máquina virtual ART, así como en la Java Virtual Machine estándar.
A diferencia de los lenguajes con gestión manual de memoria (C, C++), donde el programador debe llamar explícitamente a free o delete, GC asume completamente la tarea de rastrear el ciclo de vida de los objetos. El desarrollador crea nuevos objetos mediante el operador new, mientras que el recolector determina cuándo un objeto se vuelve inalcanzable — es decir, cuando no quedan referencias activas al mismo.
Las métricas principales de la eficiencia de GC son el tiempo de pausa (pause time) y el rendimiento (throughput). La pausa es el período durante el cual se detiene la ejecución de la aplicación para realizar la recolección. En un entorno móvil, las pausas superiores a 8–16 milisegundos son perceptibles como fotogramas perdidos (jank).
Según Google I/O 2019, ART en Android 10 redujo las pausas típicas de GC a 2–4 ms, lo que supone un 70% menos en comparación con Dalvik en Android 4.4. No obstante, la gestión inadecuada de la memoria — asignación frecuente de objetos en bucles, creación de instancias temporales innecesarias — sigue siendo la principal causa de problemas de rendimiento.
Todas las implementaciones de GC en Java y Android se basan en varios algoritmos fundamentales que se combinan para lograr un equilibrio entre el tiempo de pausa y la exhaustividad de la limpieza. Comprender estos algoritmos es esencial para escribir código compatible con GC.
Mark-and-Sweep es el algoritmo más simple, que funciona en dos etapas. En la fase de Mark, el recolector recorre el grafo de objetos comenzando desde las referencias raíz (root set) — variables locales, campos estáticos, pilas de hilos. Cada objeto alcanzable se marca con un indicador de vivo. En la fase de Sweep, el recolector recorre todo el heap y libera la memoria de los objetos no marcados.
La desventaja es la fragmentación de la memoria: después de Sweep, las áreas libres se alternan con las ocupadas, dificultando la asignación de objetos grandes. En escenarios móviles, esto es crítico ya que el heap suele ser pequeño (64–512 MB en Android).
Copying Collection divide el heap en dos semiespacios (semi-spaces). Los objetos activos se copian de forma compacta de un semiespacio al otro, sin huecos. Después de la copia, el semiespacio antiguo se declara completamente libre. El algoritmo elimina por completo la fragmentación, pero requiere el doble de memoria.
En entornos móviles, Copying Collection es utilizado por los recolectores generacionales para la limpieza rápida de objetos jóvenes, que estadísticamente mueren pronto (hipótesis débil generacional).
Generational Collection divide el heap en generaciones: Young Generation (objetos jóvenes) y Old Generation (objetos viejos que han sobrevivido varias recolecciones). La recolección de la generación joven (Minor GC) se realiza con frecuencia y rapidez, ya que la mayoría de los objetos mueren jóvenes. La recolección de la generación vieja (Major GC o Full GC) ocurre con menos frecuencia pero dura más.
// Demostración de GC generacional: los objetos jóvenes mueren rápido
void processItems(List<Item> items) {
List<Result> results = new ArrayList<>(); // vive durante todo el método
for (Item item : items) {
Result r = new Result(item.getValue()); // muere instantáneamente
if (r.isValid()) {
process(r); // r se convierte en basura
}
}
saveResults(results); // results pasa a Old Gen
}
En este ejemplo, los objetos Result se crean dentro de un bucle y se convierten inmediatamente en basura — son candidatos ideales para Young GC. El objeto results vive más tiempo y migra a Old Generation. La separación de generaciones permite que Minor GC limpie objetos jóvenes en milisegundos sin tocar el heap antiguo.
Android evolucionó de Dalvik VM a ART (Android Runtime), y la implementación de GC es una de las diferencias clave entre ellos. Comprender la arquitectura de GC en Android ayuda a escribir código que minimice las pausas en dispositivos reales.
| Característica | Dalvik (hasta 4.4) | ART (5.0+) |
|---|---|---|
| Tipo de GC | Mark-and-Sweep con Concurrent Mark | Generacional + Concurrente |
| Pausa típica | 10–30 ms | 2–4 ms |
| Compactación | No (solo crece la fragmentación) | Sí (en segundo plano, sin detener la app) |
| Compilación AOT | JIT (Just-In-Time) | AOT + JIT (híbrido) |
Dalvik utilizaba una combinación de Mark-and-Sweep con una fase concurrente. Concurrent Mark permitía que la aplicación continuara funcionando durante el recorrido del grafo de objetos, pero la fase Sweep requería detener todos los hilos (Stop-The-World). En dispositivos con poca RAM (512 MB — 1 GB), las pausas alcanzaban los 30 ms, causando retrasos notables en la interfaz. Además, Dalvik no compactaba el heap, por lo que después de un uso prolongado, la fragmentación aumentaba y la asignación de objetos grandes (por ejemplo, Bitmap) podía lanzar un OutOfMemoryError incluso con suficiente memoria libre total.
ART (Android Runtime) introdujo un recolector generacional con compactación concurrente. El heap se divide en tres regiones: Young, Mature (análogo a Old Generation) y Large Object Space (para objetos de más de 12 KB). La recolección de la región Young ocurre en paralelo sin detener los hilos en la mayoría de los casos. En Android 10+, se introdujo Concurrent Copying — la compactación se ejecuta en un hilo de fondo sin Stop-The-World.
Gracias a la arquitectura de ART, las pausas típicas de GC se redujeron a 2–4 ms, y en escenarios con predominio de objetos jóvenes — a 0.5–1 ms. Esto permitió que los dispositivos Android mantuvieran 60 FPS estables incluso durante operaciones activas de memoria.
En el ecosistema Java, existen varias implementaciones de GC, cada una con su propio perfil de rendimiento. Para el desarrollo de Android, la elección se limita a ART, pero el conocimiento de Java GC es útil al escribir código del lado del servidor para aplicaciones móviles y al desarrollar con Kotlin Multiplatform.
Serial GC es un recolector de un solo hilo con detención completa de la aplicación (Stop-The-World). Cada operación de Mark, Sweep y Compact se realiza con un solo hilo. El rendimiento es bajo — no se utiliza para servidores móviles. Solo es adecuado para aplicaciones pequeñas con un heap de hasta 100 MB.
Parallel GC (también conocido como Throughput Collector) utiliza múltiples hilos para todas las fases de recolección. Está orientado al máximo rendimiento (throughput) — minimizando el tiempo dedicado a GC en relación con el tiempo de ejecución de la aplicación. Se activa mediante el flag -XX:+UseParallelGC en JVM.
G1 (Garbage-First) GC es el recolector predeterminado en Java 9+. El heap se divide en regiones de 1–32 MB. G1 predice el tiempo de pausa y se esfuerza por mantenerse dentro de un límite especificado (por defecto 200 ms). Prioridad: las regiones con mayor cantidad de basura se limpian primero (de ahí el nombre). G1 es efectivo para servidores con grandes heaps (4–64 GB) con pausas predecibles.
// Habilitación de G1 GC con pausa objetivo de 100 ms
// java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar app.jar
public class MemoryMonitor {
private static final long THRESHOLD = 512 * 1024 * 1024; // 512 MB
public void checkHeapUsage() {
Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory();
if (used > THRESHOLD) {
System.out.println("Heap usage exceeded threshold: " + used);
System.out.println("Consider reducing allocations");
}
}
}
Monitorear el heap a través de Runtime permite detectar fugas de memoria en una etapa temprana. Si used supera el 80% del heap máximo en funcionamiento estable — esto es una señal de posible fuga o consumo excesivo de memoria de la aplicación.
Incluso el ART GC moderno no resuelve todos los problemas — el uso inadecuado de la memoria sigue siendo la causa principal de jank y ANR (Application Not Responding). Veamos los escenarios principales y los métodos de optimización.
Las pausas de GC — detenciones de los hilos de la aplicación durante la recolección. En la pantalla, esto se manifiesta como fotogramas perdidos, cuando el tiempo entre dos fotogramas supera los 16.6 ms (60 FPS). Si GC dura 30 ms, solo se dibuja un fotograma en lugar de dos — el usuario ve tartamudeo en la interfaz.
Las principales causas de pausas largas: una gran cantidad de objetos vivos en Old Generation, fragmentación del heap, Full GC frecuente. Para el diagnóstico se utilizan Android Studio Profiler y systrace.
La regla principal del código compatible con GC es minimizar la cantidad de objetos asignados. Cada nuevo objeto requiere no solo la asignación de memoria, sino también una recolección posterior. Incluso si GC es rápido, 1000 asignaciones adicionales por segundo resultan en 1000 comprobaciones para el recolector.
Una fuga de memoria ocurre cuando un objeto permanece alcanzable aunque ya no sea necesario. GC no puede eliminar dicho objeto y la memoria se agota gradualmente. Causas típicas: listeners no dados de baja, referencias estáticas a Activity, clases anónimas que capturan el contexto externo y Cursor/InputStream sin cerrar.
// Fuga de memoria: clase anónima mantiene referencia a Activity
public void startTask() {
new Thread(new Runnable() { // mantiene implícitamente this (Activity)
@Override
public void run() {
// operación larga...
System.out.println("Done");
}
}).start();
}
// Corrección: clase estática anidada + WeakReference
private static class TaskRunnable implements Runnable {
private WeakReference<Activity> activityRef;
TaskRunnable(Activity activity) {
this.activityRef = new WeakReference<>(activity);
}
@Override
public void run() {
Activity act = activityRef.get();
if (act != null) {
// trabajo seguro con Activity
}
}
}
En este ejemplo, el Runnable anónimo captura una referencia implícita a la Activity. Mientras el hilo esté vivo — Activity no puede ser recolectada por GC, incluso si el usuario ya cerró la pantalla. La corrección con WeakReference + clase estática rompe esta cadena y permite que la Activity sea liberada.
Preguntas frecuentes
GC en Android (ART) es un recolector generacional con compactación concurrente, optimizado para dispositivos móviles con memoria limitada. Java GC (G1, ZGC) son recolectores del lado del servidor con grandes heaps y pausas predecibles. ART GC no utiliza flags de JVM — toda la configuración se realiza automáticamente a nivel del SO.
Stop-The-World es el momento en que el recolector pausa todos los hilos de la aplicación para recorrer el grafo de objetos o liberar memoria de forma segura. Cuanto más largo es el STW, más notable es el jank. ART redujo el tiempo típico de STW a 2–4 ms gracias a su arquitectura generacional.
Use Android Studio Memory Profiler — muestra el crecimiento del heap, la cantidad de asignaciones y permite hacer Heap Dump. Para un análisis profundo, use LeakCanary — la biblioteca detecta automáticamente fugas y muestra la cadena de referencias que impiden la recolección de GC.
Full GC es una recolección completa de todas las generaciones del heap, incluida Old Generation. En aplicaciones móviles, Full GC puede durar 50–200 ms, causando jank o ANR notables. Causas principales: fragmentación del heap, fugas de memoria, superación del umbral de Old Generation.
Kotlin proporciona corrutinas con concurrencia estructurada — la cancelación del ámbito cancela automáticamente todas las corrutinas hijas, evitando fugas. Kotlin también tiene el delegado lazy para inicialización diferida y funciones de ámbito que reducen el número de objetos temporales.
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.