1. Confirmar que es una fuga
- Mirar métricas en el tiempo: RSS del proceso vs heap usado después de cada GC. Si el heap post-GC sube sin parar con carga estable, es sospechoso.
- Si crece el RSS pero no el heap, apuntar a memoria nativa/off-heap (buffers, librerías nativas, threads).
- Correlacionar con deploys, endpoints o jobs: ¿empezó con una versión concreta?
En staging, con carga parecida, tomar dos o más heap dumps separados en el tiempo y compararlos: lo que crece entre snapshots es el candidato.
# Java
jcmd <pid> GC.heap_dump /tmp/heap1.hprof
# Node.js
node --heapsnapshot-signal=SIGUSR2 app.js
kill -USR2 <pid>3. Causas típicas
- Cachés o Maps estáticos sin límite ni expiración.
- Listeners o suscripciones que se agregan y nunca se quitan.
- Closures que retienen objetos grandes; ThreadLocal en pools de threads.
- Conexiones, streams o timers sin cerrar.
Arreglar la causa (caché acotada tipo LRU con TTL, liberar recursos con try-with-resources o finally), validar con una prueba de carga larga (soak test) y dejar alertas sobre memoria post-GC. Reiniciar periódicamente es un parche, no la solución.
Errores comunes
Subir el límite de memoria o programar reinicios sin buscar la causa. Analizar un único heap dump en vez de comparar varios, o confundir el crecimiento normal del heap antes del GC con una fuga.
Redactado con el asistente Underc0de IA. Actualizado el 15/11/2026. ¿Ves un error? Contanos en el foro.