
Una de las cosas que primero te enseñan al aprender Java es que esta plataforma gestiona la memoria automáticamente. Los objetos que ya no se usan se limpian sin que tengas que hacer nada ni preocuparte de las fugas de memoria. Para eso están los recolectores de basura, ¿verdad? G1, ZGC, Serial, Parallel, Concurrent Mark-Sweep, Shenandoah... todos limpian y sacan brillo a la memoria de tu programa, recogiendo la "basura" para liberar espacio.
Sin embargo, existe un recolector de basura en Java que no hace nada de eso. Ni una pizca. Todo lo contrario: deja que la memoria se llene. Pero, ¿cómo es posible? Este recolector existe y llegó hace muchos años con el JDK 11. Se llama Epsilon GC. Cuando descubrí su existencia me dejó muy sorprendido ¿para qué demonios queremos esto?
Pues no es un fallo de los desarrolladores de la JVM. Es una herramienta creada intencionadamente.
Vamos a ver brevemente qué es Epsilon, cuál es el sentido de su existencia y cómo le puedes sacar partido en ciertos escenarios que quizás no habías considerado.
¿Qué es Epsilon GC y por qué existe?
Piensa en Epsilon como el anti-recolector de basura. Su función principal es ser pasivo. No activa ningún proceso para buscar objetos muertos y liberar su espacio. Cuando una aplicación Java usa Epsilon, los objetos se crean en el heap. La memoria se ocupa. Y eso es todo. La memoria nunca se recupera.
El "heap" o "montón" es una área de la memoria donde se almacenan datos de forma dinámica, permitiendo la creación y eliminación flexible de objetos durante la ejecución de un programa.
Parece una locura, ¿verdad? Pero tiene una lógica muy sólida. La razón de su existencia no es gestionar la memoria en producción de la manera normal, sino que es una herramienta para investigar el rendimiento de nuestras aplicaciones.
Los desarrolladores de la JVM lo crearon para poder aislar el coste de la asignación de memoria. Sirve para saber exactamente cuánto tiempo tarda un programa en pedir y recibir espacio para cada nuevo objeto, sin que el ruido de la recolección de basura interfiera. Es una manera de medir la "velocidad pura" de estos procesos.
Además, es perfecta para pruebas de estrés de memoria. ¿Quieres saber cómo reacciona tu aplicación cuando el heap se empieza a llenar mucho? Epsilon te garantiza que se llenará y que fallará con un OutOfMemoryError
en el momento en que se acabe el espacio. Nos permite entender los patrones de consumo de memoria de nuestras apps sin variables ocultas.
Este comportamiento único lo hace muy valioso en situaciones muy concretas, y seguramente no muy comunes. Situaciones que quizás no te imaginas...
Casos donde Epsilon es tu mejor amigo
Imagina que estás haciendo pruebas de rendimiento muy finas. Quieres medir exactamente cuánto tarda una operación que asigna mucha memoria. Con los otros recolectores "normales", las pausas de recolección de basura pueden distorsionar tus mediciones. Epsilon elimina ese ruido. Te da el coste puro de pedir memoria al sistema operativo.
También es útil para cazar fugas de memoria o entender su consumo real. Si tu aplicación tiene una fuga, otros recolectores pueden tardar en hacerla visible. Epsilon garantiza que la memoria se llenará y colapsará. Puedes ver exactamente qué está pasando justo antes del OutOfMemoryError
. Es una forma radical pero útil de perfilar el uso de memoria de un programa.
Otra utilidad que seguramente te sorprenderá es la de poder deshabilitar la recolección de memoria en programas de ejecución corta que requieren el máximo rendimiento. Las típicas utilidades de línea de comandos que necesitas que se ejecuten lo más rápido posible y terminen. Si sabes que tu programa va a vivir milisegundos, quizás no necesitas gastar ciclos de CPU en recolectar basura que nunca llegará a acumularse demasiado. Epsilon desactiva ese proceso y arañas unos milisegundos a la hora de ejecutar el programa. La memoria se libera sola automáticamente cuando el programa termina. Esta es una gran utilidad más del día a día.
Un pequeño ejemplo práctico.
Para decirle a la JVM que quieres usar Epsilon, necesitamos un par de flags del runtime de Java (java
) al ejecutar nuestra aplicación. Como Epsilon se considera una opción experimental, el primero se usa para desbloquear esas opciones. Después, simplemente seleccionamos Epsilon como el recolector a usar: -XX:+UnlockExperimentalVMOptions
y -XX:+UseEpsilonGC
.
Ahora, para que ver el efecto de que no haya recolección, usaremos este pequeño programa Java:
import java.util.ArrayList;
import java.util.List;
public class EpsilonDemo {
public static void main(String[] args) {
System.out.println("Preparando para asignar memoria...");
// Una lista para mantener referencias y llenar el heap
List<byte[]> memoryHog = new ArrayList<>();
int size = 1024 * 1024; // Bloques de 1MB
try {
System.out.println("Iniciando asignación de memoria con Epsilon GC...");
int allocatedMB = 0;
while (true) {
byte[] chunk = new byte[size]; // Asigna 1MB
memoryHog.add(chunk); // Mantiene la referencia
allocatedMB++;
if (allocatedMB % 100 == 0) {
System.out.println("Asignados aproximadamente " + allocatedMB + " MB");
}
// Una pequeña pausa para no ir demasiado rápido en algunas máquinas
// Aunque con Epsilon, la OOM llegará igual.
// Thread.sleep(1);
}
} catch (OutOfMemoryError e) {
System.out.println("\n¡Capturado OutOfMemoryError como se esperaba!");
System.out.println("La asignación de memoria se detuvo después de asignar aproximadamente " + memoryHog.size() + " MB");
}
System.out.println("Programa terminado (después de la OOM).");
}
}
Compila este código (javac EpsilonDemo.java
). Luego, ejecútalo asignándole un tamaño de heap limitado para ver el efecto rápido. Por ejemplo, con un heap máximo de 256MB:
java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xmx256m EpsilonDemo
Verás cómo el programa empieza a asignar memoria, imprimiendo el progreso. Como Epsilon no libera nada, al final la JVM se quedará sin espacio y lanzará un OutOfMemoryError
. Es la prueba de que no recolecta.
Este comportamiento predecible es justo lo que necesitas en los escenarios que mencionamos antes. Pero no todo es color de rosa. Usar Epsilon tiene sus límites y no es para cualquier tarea.
Conclusiones
Epsilon GC es una herramienta poco habitual, pero fascinante y con usos muy concretos. Aunque lleva tiempo en el JDK, se mantiene bajo la etiqueta de experimental. Esto implica que puede haber cambios en el futuro o que su soporte no es tan prioritario como el de los recolectores principales. Pero es interesante saber que está ahí.
Conocer su existencia y propósito amplía tu comprensión de la JVM, pero úsalo con precaución y solo cuando sepas exactamente por qué lo necesitas.
Una curiosidad interesante de Java, que nos muestra que a veces un componente que hace justo lo contrario de lo que se espera de él tiene también sus usos prácticos y sus limitaciones.
Espero que te haya parecido interesante.