- 1 1. Introducción
- 2 2. ¿Qué es el heap de Java?
- 2.1 2-1. Visión General de las Áreas de Memoria de Java
- 2.2 2-2. El Rol del Heap
- 2.3 2-3. ¿Qué Ocurre Cuando el Heap se Agota?
- 2.4 2-4. “Simplemente Aumentar el Heap” es a la Vez Correcto y Incorrecto
- 2.5 2-5. Distribución del Heap (Eden / Survivor / Old)
- 2.6 2-6. Por Qué la Falta de Heap es Común para Principiantes y Desarrolladores Intermedios
- 3 3. Causas Comunes del Error “java heap space”
- 3.1 3-1. Presión de Memoria por Carga de Datos Grandes
- 3.2 3-2. Acumulación excesiva de datos en colecciones
- 3.3 3-3. Fugas de memoria (retención no intencionada de objetos)
- 3.4 3-4. Tamaño del heap de JVM demasiado pequeño (los valores predeterminados son pequeños)
- 3.5 3-5. Patrones de ejecución prolongada donde los objetos siguen acumulándose
- 3.6 3-6. Malentendidos de los límites en contenedores (Docker / Kubernetes)
- 4 4. Cómo comprobar el tamaño del heap
- 4.1 4-1. Comprobar el tamaño del heap desde la línea de comandos
- 4.2 4-2. Comprobar el tamaño del heap desde dentro de un programa en ejecución
- 4.3 4-3. Comprobar usando herramientas como VisualVM o Mission Control
- 4.4 4-4. Comprobar en IDEs (Eclipse / IntelliJ)
- 4.5 4-5. Comprobar en servidores de aplicaciones (Tomcat / Jetty)
- 4.6 4-6. Comprobación del Heap en Docker / Kubernetes (Importante)
- 4.7 4-7. Resumen: Comprobar el Heap es el Primer Paso Obligatorio
- 5 5. Solución #1: Incrementar el Tamaño del Heap
- 5.1 5-1. Incrementar el Tamaño del Heap desde la Línea de Comandos
- 5.2 5-2. Configuración para Aplicaciones de Servidor Residentes (Tomcat / Jetty, etc.)
- 5.3 5-3. Configuración del Heap para Aplicaciones Spring Boot
- 5.4 5-4. Configuración del Heap en Docker / Kubernetes (Importante)
- 5.5 5-5. Ejemplos de Configuración del Heap para CI/CD y Entornos en la Nube
- 5.6 5-6. ¿Aumentar el Montón Siempre lo Soluciona? → Hay Límites
- 6 6. Solución #2: Optimiza Tu Código
- 6.1 6-1. Repensa Cómo Usas las Colecciones
- 6.2 6-2. Evita el Procesamiento en Masa de Datos Grandes (Procesa en Fragmentos)
- 6.3 6-3. Evita la Creación Innecesaria de Objetos
- 6.4 6-4. Ten Cuidado con la Concatenación de Strings
- 6.5 6-5. No sobreconstruir cachés
- 6.6 6-6. No recrear objetos dentro de bucles grandes
- 6.7 6-7. Dividir trabajo intensivo en memoria en procesos separados
- 6.8 6-8. La optimización del código es un paso clave para prevenir la recurrencia
- 7 7. Solución #3: Afinar GC (Recolección de basura)
- 8 7-1. ¿Qué es GC (Recolección de basura)?
- 9 7-2. Tipos de GC y características (Cómo elegir)
- 10 7-3. Cómo cambiar explícitamente el GC
- 11 7-4. Registrar los logs de GC y observar visualmente los problemas
- 12 7-5. Casos en los que la latencia del GC desencadena “java heap space”
- 13 7-6. Puntos clave al ajustar G1GC
- 14 7-7. Resumen del ajuste de GC
- 15 8. Solución #4: Detectar fugas de memoria
- 16 8-1. ¿Qué es una fuga de memoria? (Sí, ocurre en Java)
- 17 8-2. Patrones típicos de fugas de memoria
- 18 8-3. Puntos de Verificación para Detectar “Indicadores” de una Fuga de Memoria
- 19 8-4. Herramienta #1: Verificar Visualmente Fugas con VisualVM
- 20 8-5. Herramienta #2: Análisis Profundo con Eclipse MAT (Memory Analyzer Tool)
- 21 8-6. Si Entiendes el Dominator Tree, el Análisis se Acelera Dramáticamente
- 22 8-7. Cómo Capturar un Volcado de Heap (Línea de Comandos)
- 23 8-8. La Corrección Real de una Fuga Requiere Cambios de Código
- 24 8-9. Cómo Diferenciar “Escasez de Heap” vs “Fuga de Memoria”
- 25 8-10. Resumen: Si el Ajuste del Heap No Resuelve el OOM, Sospecha una Fuga
- 26 9. Problemas de “java heap space” en Docker / Kubernetes y Cómo Solucionarlos
- 27 9-1. Por qué los errores de Heap Space son tan comunes en contenedores
- 28 9-2. Mejoras en Java 8u191+ y Java 11+
- 29 9-3. Configuración explícita del tamaño del heap en contenedores (obligatoria)
- 30 9-4. Trampas de configuración de memoria en Kubernetes (K8s)
- 31 9-5. Porcentaje automático del heap en Java 11+ (MaxRAMPercentage)
- 32 9-6. Por qué OOMKilled ocurre tan a menudo en contenedores (patrón del mundo real)
- 33 9-7. Puntos de control específicos de contenedores usando logs de GC y métricas
- 34 9-8. Resumen: “Configuraciones Explícitas” Son el Valor Predeterminado en Contenedores
- 35 10. Anti‑Patrones a Evitar (Código Defectuoso / Configuraciones Incorrectas)
- 36 10-1. Dejar que Colecciones Sin Límite Crezcan Indefinidamente
- 37 10-2. Cargar Archivos o Datos Enormes de una Sola Vez
- 38 10-3. Continuar Manteniendo Datos en Variables estáticas
- 39 10-4. Abusar de Stream / Lambda y Generar Listas Intermedias Enormes
- 40 10-5. Realizar Concatenaciones Masivas de Cadenas con el Operador +
- 41 10-6. Crear Demasiadas Cachés y No Gestionarlas
- 42 10-7. Mantener registros o estadísticas en memoria continuamente
- 43 10-8. No especificar -Xmx en contenedores Docker
- 44 10-9. Sobreajustar la configuración del GC
- 45 10-10. Resumen: La mayoría de los anti‑patrones provienen de “Almacenar demasiado”
- 46 11. Ejemplos reales: Este código es peligroso (Patrones típicos de problemas de memoria)
- 47 11-1. Carga masiva de datos enormes
- 48 11-2. Patrón de hinchazón de colecciones
- 49 11-3. Generar demasiados objetos intermedios mediante la API Stream
- 50 Q10. ¿Cómo puedo evitar que los problemas de heap en Java se repitan?
- 51 Resumen: Usa el FAQ para eliminar dudas y aplicar contramedidas prácticas de memoria
1. Introducción
Cuando desarrollas en Java, ¿alguna vez tu aplicación se ha bloqueado de repente y la consola muestra:
java.lang.OutOfMemoryError: Java heap space
Este error significa “Java se ha quedado sin memoria utilizable (el heap).”
Sin embargo, solo con el mensaje de error no es evidente de inmediato:
- Qué provocó que el heap se agotara
- Qué deberías ajustar y cómo
- Si el problema está en el código o en la configuración
Como resultado, la gente suele recurrir a “soluciones rápidas” como “simplemente aumentar -Xmx” o “añadir más memoria al servidor.”
Pero aumentar el tamaño del heap sin entender la causa raíz no solo no es una solución real, sino que también puede generar otros problemas.
- La GC (recolección de basura) se vuelve más pesada y los tiempos de respuesta se degradan
- La memoria total del servidor se vuelve escasa y afecta a otros procesos
- Una fuga de memoria real permanece, y el
OutOfMemoryErrorvuelve a ocurrir
Por eso “java heap space” no es simplemente “poca memoria”.
Debes considerarlo como una señal de un problema compuesto que involucra el diseño de la aplicación, la implementación y la configuración de la infraestructura.
1-1. Público objetivo
Este artículo está dirigido a lectores que:
- Entienden los conceptos básicos de Java (clases, métodos, colecciones, etc.)
- Pero no comprenden completamente cómo se gestiona la memoria dentro de la JVM
- Han encontrado “java heap space” o
OutOfMemoryErroren desarrollo/pruebas/producción — o quieren estar preparados - Ejecutan Java en Docker/contenedores/nube y se sienten ligeramente inseguros sobre la configuración de memoria
Tus años de experiencia con Java no importan.
Si deseas “entender correctamente el error y aprender a aislar la causa por ti mismo”, esta guía pretende ser directamente útil en el trabajo real.
1-2. Qué aprenderás en este artículo
En este artículo explicamos el error “java heap space” desde el mecanismo hacia arriba, no solo una lista de soluciones.
Los temas clave incluyen:
Qué es el heap de Java wp:list /wp:list
- En qué se diferencia de la pila
- Dónde se asignan los objetos
Patrones comunes que provocan “java heap space” wp:list /wp:list
Carga masiva de datos grandes
- Crecimiento excesivo de colecciones y cachés
- Fugas de memoria (código que mantiene referencias vivas)
Cómo comprobar y aumentar el tamaño del heap wp:list /wp:list
Opciones de línea de comandos (
-Xms,-Xmx)- Configuraciones en IDEs (Eclipse / IntelliJ, etc.)
- Puntos de configuración del servidor de aplicaciones (Tomcat, etc.)
Técnicas para ahorrar memoria en el código wp:list /wp:list
Revisar el uso de colecciones
- Trampas al usar streams y lambdas
- Estrategias de fragmentación para datos grandes
La relación entre la GC y el heap wp:list /wp:list
Cómo funciona básicamente la GC
- Cómo leer los logs de GC a nivel básico
Detección de fugas de memoria y uso de herramientas wp:list /wp:list
Obtención de un volcado de heap
- Introducción al análisis con VisualVM o Eclipse MAT
Cosas a vigilar en entornos de contenedores (Docker / Kubernetes) wp:list /wp:list
La relación entre los contenedores y
-Xmx- Límites de memoria mediante cgroups y el OOM Killer
En la segunda mitad del artículo, también respondemos preguntas frecuentes en formato FAQ, como:
- “¿Debo simplemente aumentar el heap por ahora?”
- “¿Hasta dónde puedo aumentar el heap de forma segura?”
- “¿Cómo puedo estimar si se trata de una fuga de memoria?”
1-3. Cómo leer este artículo
El error “java heap space” es importante tanto para personas que:
- Necesitan solucionar un incidente de producción ahora mismo
- Quieren prevenir problemas antes de que ocurran
Si necesitas una solución inmediata, puedes saltar a las secciones prácticas, como:
- Cómo cambiar el tamaño del heap
- Cómo comprobar fugas de memoria
Por otro lado, si deseas una comprensión profunda, lee en este orden:
- Lo básico: “Qué es el heap de Java”
- Causas típicas
- Luego soluciones y pasos de afinación
Este flujo te ayudará a entender claramente el mecanismo detrás del error.
2. ¿Qué es el heap de Java?
Para comprender correctamente el error “java heap space”, primero debes saber cómo Java gestiona la memoria.
En Java, la memoria se divide en varias áreas según su propósito, y entre ellas el heap juega un papel crítico como el espacio de memoria para los objetos.
2-1. Visión General de las Áreas de Memoria de Java
Las aplicaciones Java se ejecutan sobre la JVM (Java Virtual Machine).
La JVM cuenta con múltiples áreas de memoria para manejar diferentes tipos de datos. Las tres más comunes son:
■ Tipos de Áreas de Memoria
- Heap: el área donde se almacenan los objetos creados por la aplicación. Si se agota, aparece el error “java heap space”.
- Stack: el área para llamadas a métodos, variables locales, referencias, etc. Si se desborda, se produce un “StackOverflowError”.
- Method Area / Metaspace: almacena información de clases, constantes, metadatos y resultados de compilación JIT.
En Java, todos los objetos creados con new se colocan en el heap.
2-2. El Rol del Heap
El heap de Java es donde se guardan cosas como las siguientes:
- Objetos creados con
new - Arreglos (incluido el contenido de List/Map, etc.)
- Objetos generados internamente por lambdas
- Cadenas y buffers usados por StringBuilder
- Estructuras de datos utilizadas dentro del framework de colecciones
En otras palabras, cuando Java necesita “mantener algo en memoria”, casi siempre se almacena en el heap.
2-3. ¿Qué Ocurre Cuando el Heap se Agota?
Si el heap es demasiado pequeño —o la aplicación crea demasiados objetos— Java ejecuta GC (recolección de basura) para recuperar memoria eliminando objetos no utilizados.
Pero si el GC repetido aún no libera suficiente memoria y la JVM ya no puede asignar más, obtendrás:
java.lang.OutOfMemoryError: Java heap space
y la aplicación se verá obligada a detenerse.
2-4. “Simplemente Aumentar el Heap” es a la Vez Correcto y Incorrecto
Si el heap es simplemente demasiado pequeño, aumentarlo puede resolver el problema —por ejemplo:
-Xms1024m -Xmx2048m
Sin embargo, si la causa raíz es una fuga de memoria o un procesamiento ineficiente de datos masivos en el código, aumentar el heap solo compra tiempo y no soluciona el problema subyacente.
En resumen, entender “por qué el heap se está agotando” es lo más importante.
2-5. Distribución del Heap (Eden / Survivor / Old)
El heap de Java se divide en dos partes principales:
Generación joven (objetos recién creados) wp:list /wp:list
- Eden
- Survivor (S0, S1)
- Generación vieja (objetos de larga vida)
El GC funciona de manera diferente según el área.
Generación joven
Los objetos se colocan primero en Eden, y los objetos de corta vida se eliminan rápidamente.
El GC se ejecuta con frecuencia aquí, pero es relativamente liviano.
Generación vieja
Los objetos que sobreviven lo suficiente son promovidos de Young a Old.
El GC en Old es más costoso, por lo que si esta área sigue creciendo, puede causar latencia o pausas.
En muchos casos, un error “heap space” ocurre finalmente porque la generación Old se llena.
2-6. Por Qué la Falta de Heap es Común para Principiantes y Desarrolladores Intermedios
Debido a que Java realiza la recolección de basura automáticamente, la gente suele asumir que “la JVM maneja toda la gestión de memoria”.
En realidad, existen muchas formas de quedarse sin heap, como:
- Código que sigue creando gran número de objetos
- Referencias mantenidas vivas dentro de colecciones
- Streams/lambdas que generan involuntariamente datos masivos
- Cachés que crecen sin control
- Malentendidos sobre los límites de heap en contenedores Docker
- Configuración incorrecta del heap en un IDE
Por eso aprender cómo funciona el heap es el camino más corto hacia una solución fiable.
3. Causas Comunes del Error “java heap space”
La escasez de heap es un problema frecuente en muchos entornos reales, pero sus causas pueden agruparse en tres categorías principales: volumen de datos, código/diseño y configuración incorrecta.
En esta sección organizamos los patrones típicos y explicamos por qué conducen al error.
3-1. Presión de Memoria por Carga de Datos Grandes
El patrón más común es cuando los datos en sí son tan grandes que el heap se agota.
■ Ejemplos comunes
- Cargar un CSV/JSON/XML enorme de una sola vez en memoria
- Obtener una gran cantidad de registros de base de datos de una sola vez
- Una API web devuelve una respuesta muy grande (imágenes, registros, etc.)
Un escenario particularmente peligroso es:
Cuando la “cadena cruda antes del análisis” y los “objetos después del análisis” existen en memoria al mismo tiempo.
Por ejemplo, si cargas un JSON de 500 MB como una única cadena y luego lo deserializas con Jackson, el uso total de memoria puede superar fácilmente 1 GB.
■ Dirección para la mitigación
- Introduce lectura por fragmentos (procesamiento en streaming)
- Utiliza paginación para el acceso a la base de datos
- Evita mantener datos intermedios más tiempo del necesario
Seguir la regla “manejar datos grandes por fragmentos” ayuda mucho a prevenir el agotamiento del heap.
3-2. Acumulación excesiva de datos en colecciones
Esto es extremadamente común entre desarrolladores principiantes e intermedios.
■ Errores típicos
- Agregar continuamente logs o datos temporales a una
List→ crece sin ser limpiada - Usar un
Mapcomo caché (pero nunca expulsar entradas) - Crear nuevos objetos continuamente dentro de bucles
- Generar una gran cantidad de objetos temporales mediante Streams o lambdas
En Java, mientras exista una referencia, el GC no puede eliminar el objeto.
En muchos casos, los desarrolladores mantienen referencias vivas sin intención.
■ Dirección para la mitigación
- Define un ciclo de vida para las cachés
- Establece límites de capacidad para las colecciones
- Para mecanismos de datos grandes, limpia periódicamente
Como referencia, incluso si no “parece” una fuga de memoria:
List<String> list = new ArrayList<>();
for (...) {
list.add(heavyData); // ← grows forever
}
Este tipo de código es muy peligroso.
3-3. Fugas de memoria (retención no intencionada de objetos)
Debido a que Java tiene GC, la gente suele pensar que “las fugas de memoria no ocurren en Java.”
En la práctica, las fugas de memoria sí ocurren en Java.
■ Puntos críticos comunes de fugas
- Mantener objetos en variables estáticas
- Olvidar desregistrar listeners o callbacks
- Dejar referencias vivas dentro de Streams/Lambdas
- Objetos acumulándose en trabajos por lotes de larga duración
- Almacenar datos grandes en ThreadLocal y que el hilo sea reutilizado
Las fugas de memoria no son algo que puedas evitar completamente en Java.
■ Dirección para la mitigación
- Revisa cómo utilizas las variables estáticas
- Asegúrate de que
removeListener()yclose()se llamen siempre - Para procesos de larga duración, realiza un heap dump e investiga
- Evita ThreadLocal a menos que sea realmente necesario
Porque las fugas de memoria volverán a aparecer incluso si aumentas el heap, la investigación de la causa raíz es esencial.
3-4. Tamaño del heap de JVM demasiado pequeño (los valores predeterminados son pequeños)
A veces la aplicación está bien, pero el heap en sí es simplemente demasiado pequeño.
El tamaño predeterminado del heap varía según el SO y la versión de Java.
En Java 8, suele establecerse en aproximadamente 1/64 a 1/4 de la memoria física.
Una configuración peligrosa que se ve a menudo en producción es:
No -Xmx specified, while the app processes large data
■ Escenarios comunes
- Solo en producción hay mayor volumen de datos, y el heap predeterminado no es suficiente
- Ejecutar en Docker sin establecer
-Xmx - Spring Boot iniciado como un JAR gordo con valores predeterminados
■ Dirección para la mitigación
- Establece
-Xmsy-Xmxa valores apropiados - En contenedores, comprende memoria física vs límites de cgroup y configúralos en consecuencia
3-5. Patrones de ejecución prolongada donde los objetos siguen acumulándose
Aplicaciones como las siguientes tienden a acumular presión de memoria con el tiempo:
- Aplicaciones Spring Boot de larga duración
- Trabajos por lotes intensivos en memoria
- Aplicaciones web con gran tráfico de usuarios
Los trabajos por lotes, en particular, a menudo muestran este patrón:
- Se consume memoria
- El GC apenas recupera suficiente
- Alguna acumulación permanece, y la siguiente ejecución provoca OOM
Esto conduce a muchos errores de espacio de heap de aparición tardía.
3-6. Malentendidos de los límites en contenedores (Docker / Kubernetes)
Hay una trampa común en Docker/Kubernetes:
■ Trampa
- No establecer
-Xmx→ Java hace referencia a la memoria física del host en lugar del límite del contenedor → usa demasiado → el proceso es terminado por el OOM Killer
Esto es uno de los incidentes de producción más comunes.
■ Mitigación
- Establecer
-XX:MaxRAMPercentagede forma adecuada - Alinear
-Xmxcon el límite de memoria del contenedor - Entender “UseContainerSupport” en Java 11+
4. Cómo comprobar el tamaño del heap
Cuando veas un error de “java heap space”, lo primero que debes hacer es confirmar cuánto heap está asignado actualmente.
En muchos casos, el heap simplemente es más pequeño de lo esperado, por lo que comprobarlo es un paso crítico inicial.
En esta sección, cubrimos formas de comprobar el tamaño del heap desde la línea de comandos, dentro del programa, IDEs y servidores de aplicaciones.
4-1. Comprobar el tamaño del heap desde la línea de comandos
Java ofrece varias opciones para comprobar los valores de configuración de la JVM al iniciar.
■ Usando -XX:+PrintFlagsFinal
Esta es la forma más fiable de confirmar el tamaño del heap:
java -XX:+PrintFlagsFinal -version | grep HeapSize
Verás una salida similar a:
- InitialHeapSize … el tamaño inicial del heap especificado por
-Xms - MaxHeapSize … el tamaño máximo del heap especificado por
-Xmx
Ejemplo:
uintx InitialHeapSize = 268435456
uintx MaxHeapSize = 4294967296
Esto significa:
- Heap inicial: 256 MB
- Heap máximo: 4 GB
■ Ejemplo concreto
java -Xms512m -Xmx2g -XX:+PrintFlagsFinal -version | grep HeapSize
Esto también es útil después de cambiar la configuración, convirtiéndolo en un método de confirmación fiable.
4-2. Comprobar el tamaño del heap desde dentro de un programa en ejecución
A veces quieres comprobar la cantidad de heap desde dentro de la aplicación en ejecución.
Java lo hace fácil usando la clase Runtime:
long max = Runtime.getRuntime().maxMemory();
long total = Runtime.getRuntime().totalMemory();
long free = Runtime.getRuntime().freeMemory();
System.out.println("Max Heap: " + (max / 1024 / 1024) + " MB");
System.out.println("Total Heap: " + (total / 1024 / 1024) + " MB");
System.out.println("Free Heap: " + (free / 1024 / 1024) + " MB");
- maxMemory() … el tamaño máximo del heap (
-Xmx) - totalMemory() … el heap actualmente asignado por la JVM
- freeMemory() … el espacio disponible actualmente dentro de ese heap
Para aplicaciones web o procesos de larga duración, registrar estos valores puede ayudar durante la investigación de incidentes.
4-3. Comprobar usando herramientas como VisualVM o Mission Control
También puedes inspeccionar visualmente el uso del heap con herramientas GUI.
■ VisualVM
- Visualización en tiempo real del uso del heap
- Tiempos de GC
- Captura de volcado de heap
Es una herramienta clásica, comúnmente usada en desarrollo Java.
■ Java Mission Control (JMC)
- Permite un perfilado más detallado
- Especialmente útil para operaciones en Java 11+
Estas herramientas te ayudan a visualizar problemas como solo el crecimiento de la generación Old.
4-4. Comprobar en IDEs (Eclipse / IntelliJ)
Si ejecutas tu aplicación desde un IDE, la configuración del IDE puede afectar el tamaño del heap.
■ Eclipse
Window → Preferences → Java → Installed JREs
O establece -Xms / -Xmx en:
Configuración de ejecución → Argumentos de VM
■ IntelliJ IDEA
Help → Change Memory Settings
O agrega -Xmx bajo opciones de VM en la Configuración de ejecución/depuración.
Ten cuidado—a veces el propio IDE impone un límite de heap.
4-5. Comprobar en servidores de aplicaciones (Tomcat / Jetty)
Para aplicaciones web, el tamaño del heap suele especificarse en los scripts de inicio del servidor.
■ Ejemplo de Tomcat (Linux)
CATALINA_OPTS="-Xms512m -Xmx2g"
■ Ejemplo de Tomcat (Windows)
set JAVA_OPTS=-Xms512m -Xmx2g
En producción, dejar esto en los valores predeterminados es común, y a menudo conduce a errores de espacio de heap después de que el servicio ha estado ejecutándose un tiempo.
4-6. Comprobación del Heap en Docker / Kubernetes (Importante)
En contenedores, memoria física, cgroups y configuraciones de Java interactúan de manera compleja.
En Java 11+, “UseContainerSupport” puede ajustar el heap automáticamente, pero el comportamiento aún puede ser inesperado según:
- El límite de memoria del contenedor (p. ej.,
--memory=512m) - Si
-Xmxestá configurado explícitamente
Por ejemplo, si solo estableces un límite de memoria del contenedor:
docker run --memory=512m ...
y no configuras -Xmx, puedes encontrarte con:
- Java hace referencia a la memoria del host y trata de asignar demasiado
- Los cgroups aplican el límite
- El proceso es terminado por el OOM Killer
Este es un problema de producción muy frecuente.
4-7. Resumen: Comprobar el Heap es el Primer Paso Obligatorio
Los déficits de heap requieren soluciones muy diferentes según la causa.
Comienza comprendiendo, como conjunto:
- El tamaño actual del heap
- El uso real
- Visualización mediante herramientas
5. Solución #1: Incrementar el Tamaño del Heap
La respuesta más directa a un error de “java heap space” es incrementar el tamaño del heap.
Si la causa es una simple escasez de memoria, aumentar el heap de forma adecuada puede restaurar el comportamiento normal.
Sin embargo, cuando incrementas el heap, es importante entender tanto
los métodos de configuración correctos como las precauciones clave.
Configuraciones incorrectas pueden provocar degradación del rendimiento u otros problemas de OOM (Out Of Memory).
5-1. Incrementar el Tamaño del Heap desde la Línea de Comandos
Si inicias una aplicación Java como JAR, el método más básico es especificar -Xms y -Xmx:
■ Ejemplo: Inicial 512 MB, Máx 2 GB
java -Xms512m -Xmx2g -jar app.jar
-Xms… el tamaño inicial del heap reservado al iniciar la JVM-Xmx… el tamaño máximo del heap que la JVM puede usar
En muchos casos, establecer -Xms y -Xmx al mismo valor ayuda a reducir la sobrecarga por el redimensionamiento del heap.
Ejemplo:
java -Xms2g -Xmx2g -jar app.jar
5-2. Configuración para Aplicaciones de Servidor Residentes (Tomcat / Jetty, etc.)
Para aplicaciones web, establece estas opciones en los scripts de arranque del servidor de aplicaciones.
■ Tomcat (Linux)
Configura en setenv.sh:
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m -Xmx2048m"
■ Tomcat (Windows)
Configura en setenv.bat:
set CATALINA_OPTS=-Xms512m -Xmx2048m
■ Jetty
Añade lo siguiente a start.ini o jetty.conf:
--exec
-Xms512m
-Xmx2048m
Debido a que las aplicaciones web pueden experimentar picos de uso de memoria según el tráfico, la producción debería contar generalmente con más margen que los entornos de prueba.
5-3. Configuración del Heap para Aplicaciones Spring Boot
Si ejecutas Spring Boot como un JAR “fat”, los conceptos básicos son los mismos:
java -Xms1g -Xmx2g -jar spring-app.jar
Spring Boot tiende a usar más memoria que un programa Java simple porque carga muchas clases y configuraciones al iniciar.
A menudo consume más memoria que una aplicación Java típica.
5-4. Configuración del Heap en Docker / Kubernetes (Importante)
Para Java en contenedores, debes ser cuidadoso porque los límites del contenedor y el cálculo del heap de la JVM interactúan.
■ Ejemplo Recomendado (Docker)
docker run --memory=1g \
-e JAVA_OPTS="-Xms512m -Xmx800m" \
my-java-app
■ Por Qué Debes Establecer Explícitamente -Xmx
Si no especificas -Xmx en Docker:
- La JVM decide el tamaño del heap basándose en la memoria física de la máquina host, no en el contenedor
- Puede intentar asignar más memoria de la que permite el contenedor
- Alcanza el límite de memoria del cgroup y el proceso es terminado por el OOM Killer
Dado que este es un problema de producción muy frecuente,
debes siempre establecer -Xmx en entornos de contenedores.
5-5. Ejemplos de Configuración del Heap para CI/CD y Entornos en la Nube
En entornos Java basados en la nube, una regla práctica común es establecer el heap en función de la memoria disponible:
| Total Memory | Recommended Heap (Approx.) |
|---|---|
| 1GB | 512–800MB |
| 2GB | 1.2–1.6GB |
| 4GB | 2–3GB |
| 8GB | 4–6GB |
※ Deja la memoria restante para el SO, el sobrecargo de GC y las pilas de hilos.
En entornos en la nube, la memoria total puede estar limitada. Si aumentas el montón sin planificar, toda la aplicación puede volverse inestable.
5-6. ¿Aumentar el Montón Siempre lo Soluciona? → Hay Límites
Aumentar el tamaño del montón puede eliminar el error temporalmente, pero no resuelve casos como:
- Existe una fuga de memoria
- Una colección sigue creciendo indefinidamente
- Se procesan datos enormes en masa
- La aplicación tiene un diseño incorrecto
Por lo tanto, trata el aumento del montón como una medida de emergencia y asegúrate de seguir con optimización de código y revisar el diseño de procesamiento de datos, que cubriremos a continuación.
6. Solución #2: Optimiza Tu Código
Aumentar el tamaño del montón puede ser una mitigación efectiva, pero si la causa raíz radica en la estructura de tu código o la forma en que procesas datos, el error de “java heap space” regresará tarde o temprano.
En esta sección, cubriremos patrones de codificación comunes del mundo real que desperdician memoria, y enfoques concretos para mejorarlos.
6-1. Repensa Cómo Usas las Colecciones
Las colecciones de Java (List, Map, Set, etc.) son convenientes, pero un uso descuidado puede convertirse fácilmente en la causa principal del crecimiento de memoria.
■ Patrón ①: List / Map Crece Sin Límites
Un ejemplo común:
List<String> logs = new ArrayList<>();
while (true) {
logs.add(fetchLog()); // ← grows forever
}
Las colecciones con ninguna condición de terminación clara o límite superior exprimirán de manera confiable el montón en entornos de ejecución prolongada.
● Mejoras
- Usa una colección acotada (por ejemplo, limita el tamaño y descarta entradas antiguas)
- Limpia periódicamente los valores que ya no necesitas
- Si usas un Map como caché, adopta un caché con desalojo → Guava Cache o Caffeine son buenas opciones
■ Patrón ②: No Establecer la Capacidad Inicial
ArrayList y HashMap crecen automáticamente cuando exceden la capacidad, pero ese crecimiento implica:
asignar un nuevo arreglo → copiar → descartar el arreglo antiguo.
Al manejar conjuntos de datos grandes, omitir la capacidad inicial es ineficiente y puede desperdiciar memoria.
● Ejemplo de Mejora
List<String> items = new ArrayList<>(10000);
Si puedes estimar el tamaño, es mejor establecerlo de antemano.
6-2. Evita el Procesamiento en Masa de Datos Grandes (Procesa en Fragmentos)
Si procesas datos masivos de una sola vez, es fácil caer en el peor escenario:
todo termina en el montón → OOM.
■ Mal Ejemplo (Leer un Archivo Enorme de Una Sola Vez)
String json = Files.readString(Paths.get("large.json"));
Object data = new ObjectMapper().readValue(json, Data.class);
■ Mejoras
- Usa procesamiento en streaming (por ejemplo, Jackson Streaming API)
- Lee en porciones más pequeñas (paginación por lotes)
- Procesa flujos secuencialmente y no retengas todo el conjunto de datos
● Ejemplo: Procesa JSON Enorme con Jackson Streaming
JsonFactory factory = new JsonFactory();
try (JsonParser parser = factory.createParser(new File("large.json"))) {
while (!parser.isClosed()) {
JsonToken token = parser.nextToken();
// Perform only what you need, and do not retain it in memory
}
}
6-3. Evita la Creación Innecesaria de Objetos
Los streams y lambdas son convenientes, pero pueden generar un gran número de objetos temporales internamente.
■ Mal Ejemplo (Crear una Lista Intermedia Enorme con Streams)
List<Result> results = items.stream()
.map(this::toResult)
.collect(Collectors.toList());
Si items es enorme, se crean un gran número de objetos temporales y el montón se infla.
● Mejoras
- Procesa secuencialmente con un bucle for
- Escribe solo lo que necesitas inmediatamente (no mantengas todo)
- Evita
collect(), o contrólalo manualmente
6-4. Ten Cuidado con la Concatenación de Strings
Los Strings de Java son inmutables, por lo que cada concatenación crea un nuevo objeto.
■ Mejoras
- Usa
StringBuilderpara concatenaciones intensivas - Evita concatenaciones innecesarias al generar logs
StringBuilder sb = new StringBuilder(); for (String s : items) { sb.append(s); }
6-5. No sobreconstruir cachés
Esta es una situación común en aplicaciones web y procesamiento por lotes:
- “Añadimos una caché para mejorar la velocidad.”
- → pero olvidamos limpiarla
- → la caché sigue creciendo
- → escasez de heap → OOM
■ Mejoras
- Establecer TTL (expiración basada en tiempo) y un tamaño máximo
- Usar
ConcurrentHashMapcomo sustituto de caché es arriesgado - Utilizar una caché bien gestionada como Caffeine que controla la memoria adecuadamente
6-6. No recrear objetos dentro de bucles grandes
■ Mal ejemplo
for (...) {
StringBuilder sb = new StringBuilder(); // created every iteration
...
}
Esto crea más objetos temporales de los necesarios.
● Mejora
StringBuilder sb = new StringBuilder();
for (...) {
sb.setLength(0); // reuse
}
6-7. Dividir trabajo intensivo en memoria en procesos separados
Cuando manejas datos realmente masivos en Java, puede ser necesario replantear la arquitectura.
- Separar ETL en un trabajo por lotes dedicado
- Delegar a procesamiento distribuido (Spark o Hadoop)
- Dividir los servicios para evitar contención del heap
6-8. La optimización del código es un paso clave para prevenir la recurrencia
Si solo aumentas el heap, eventualmente alcanzarás el siguiente “límite” y el mismo error volverá a ocurrir.
Para prevenir fundamentalmente los errores de “java heap space”, debes:
- Comprender el volumen de tus datos
- Revisar los patrones de creación de objetos
- Mejorar el diseño de las colecciones
7. Solución #3: Afinar GC (Recolección de basura)
El error “java heap space” puede ocurrir no solo cuando el heap es demasiado pequeño, sino también cuando GC no puede recuperar memoria de manera eficaz y el heap se satura gradualmente.
Sin comprender GC, puedes diagnosticar erróneamente síntomas como:
“La memoria debería estar disponible, pero seguimos obteniendo errores,” o “El sistema se vuelve extremadamente lento.”
Esta sección explica el mecanismo básico de GC en Java y puntos prácticos de afinación que ayudan en operaciones reales.
7-1. ¿Qué es GC (Recolección de basura)?
GC es el mecanismo de Java para descartar automáticamente los objetos que ya no son necesarios.
El heap de Java se divide en dos generaciones, y GC se comporta de manera diferente en cada una.
● Generación joven (objetos de corta vida)
- Eden / Survivor (S0, S1)
- Datos temporales creados localmente, etc.
- GC ocurre con frecuencia, pero es ligero
● Generación vieja (objetos de larga vida)
- Objetos promovidos desde la generación joven
- GC es más pesado; si ocurre con frecuencia, la aplicación puede “congelarse”
En muchos casos, el error “java heap space” ocurre finalmente cuando la generación vieja se llena.
7-2. Tipos de GC y características (Cómo elegir)
Java ofrece múltiples algoritmos de GC.
Elegir el adecuado para tu carga de trabajo puede mejorar significativamente el rendimiento.
● ① G1GC (Predeterminado desde Java 9)
- Divide el heap en pequeñas regiones y las recupera de forma incremental
- Puede mantener las pausas stop-the-world más cortas
- Ideal para aplicaciones web y sistemas empresariales
→ En general, G1GC es una opción predeterminada segura
● ② Parallel GC (Bueno para trabajos por lotes con alto rendimiento)
- Paralelizado y rápido
- Pero los tiempos de pausa pueden alargarse
- A menudo beneficioso para procesamiento por lotes intensivo en CPU
● ③ ZGC (GC de baja latencia con pausas a nivel de milisegundos)
- Disponible en Java 11+
- Para aplicaciones sensibles a la latencia (servidores de juegos, HFT)
- Eficaz incluso con heaps grandes (decenas de GB)
● ④ Shenandoah (GC de baja latencia)
- A menudo asociado con distribuciones Red Hat
- Puede minimizar las pausas de forma agresiva
- También disponible en algunas compilaciones como AWS Corretto
7-3. Cómo cambiar explícitamente el GC
G1GC es el predeterminado en muchas configuraciones, pero puedes especificar un algoritmo de GC según tu objetivo:
# G1GC
java -XX:+UseG1GC -jar app.jar
# Parallel GC
java -XX:+UseParallelGC -jar app.jar
# ZGC
java -XX:+UseZGC -jar app.jar
Porque el algoritmo de GC puede cambiar drásticamente el comportamiento del heap y el tiempo de pausa, los sistemas en producción a menudo lo configuran explícitamente.
7-4. Registrar los logs de GC y observar visualmente los problemas
Es fundamental entender cuánta memoria está recuperando el GC y con qué frecuencia ocurren las pausas de “stop‑the‑world”.
● Configuración básica de registro de GC
java \
-Xms1g -Xmx1g \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-Xloggc:gc.log \
-jar app.jar
Al examinar gc.log, puedes identificar señales claras de presión en el heap, como:
- Demasiados GC jóvenes
- La generación Old nunca disminuye
- GC completo ocurre con frecuencia
- Cada GC recupera una cantidad inusualmente pequeña
7-5. Casos en los que la latencia del GC desencadena “java heap space”
Si la presión del heap es causada por patrones como los siguientes, el comportamiento del GC se vuelve una pista decisiva.
● Síntomas
- La aplicación se congela repentinamente
- El GC se ejecuta durante segundos o decenas de segundos
- La generación Old sigue creciendo
- El GC completo aumenta y, finalmente, ocurre OOM
Esto indica un estado en el que el GC se esfuerza mucho, pero no puede recuperar suficiente memoria antes de alcanzar el límite.
■ Causas raíz comunes
- Fugas de memoria
- Colecciones retenidas permanentemente
- Objetos que viven demasiado tiempo
- Inflación de la generación Old
En estos casos, analizar los logs de GC puede ayudarte a identificar señales de fuga o picos de carga en momentos específicos.
7-6. Puntos clave al ajustar G1GC
G1GC es robusto por defecto, pero el ajuste puede hacerlo aún más estable.
● Parámetros comunes
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=8m
-XX:InitiatingHeapOccupancyPercent=45
- MaxGCPauseMillis → Tiempo de pausa objetivo (p. ej., 200 ms)
- G1HeapRegionSize → Tamaño de región usado para particionar el heap
- InitiatingHeapOccupancyPercent → Porcentaje de ocupación de la generación Old que desencadena un ciclo de GC
Sin embargo, en muchos casos los valores predeterminados están bien, así que solo cámbialos cuando tengas una necesidad clara.
7-7. Resumen del ajuste de GC
Las mejoras de GC te ayudan a visualizar factores que no son evidentes simplemente aumentando el tamaño del heap:
- Vida útil de los objetos
- Patrones de uso de colecciones
- Si existe una fuga de memoria
- Dónde se concentra la presión del heap
Por eso, el ajuste de GC es un proceso sumamente importante para mitigar “java heap space”.
8. Solución #4: Detectar fugas de memoria
Si el error sigue reapareciendo incluso después de aumentar el heap y optimizar el código, el sospechoso más probable es una fuga de memoria.
Muchas personas asumen que Java es resistente a las fugas de memoria porque existe el GC, pero en la práctica, las fugas son una de las causas más problemáticas y recurrentes en entornos reales.
Aquí nos centramos en pasos prácticos que puedes usar de inmediato, desde comprender las fugas hasta utilizar herramientas de análisis como VisualVM y Eclipse MAT.
8-1. ¿Qué es una fuga de memoria? (Sí, ocurre en Java)
Una fuga de memoria en Java es:
Un estado en el que permanecen referencias a objetos innecesarios, impidiendo que el GC los recupere.
Incluso con recolección de basura, las fugas suelen ocurrir cuando:
- Los objetos se mantienen en campos
static - Los listeners registrados dinámicamente nunca se desregistran
- Las colecciones siguen creciendo y retienen referencias
- Los valores de
ThreadLocalpersisten inesperadamente - Los ciclos de vida del framework no coinciden con el ciclo de vida de tus objetos
Así que las fugas son una posibilidad totalmente normal.
8-2. Patrones típicos de fugas de memoria
● ① Crecimiento de colecciones (el más común)
Agregar continuamente a List/Map/Set sin eliminar entradas.
En sistemas Java empresariales, una gran parte de los incidentes de OOM provienen de este patrón.
● ② Mantener objetos en variables estáticas
private static List<User> cache = new ArrayList<>();
Esto a menudo se convierte en el punto de partida de una fuga.
● ③ Olvidar desregistrar listeners / callbacks
Las referencias permanecen en segundo plano mediante GUI, observadores, listeners de eventos, etc.
● ④ Uso incorrecto de ThreadLocal
En entornos con pools de hilos, los valores de ThreadLocal pueden persistir más tiempo del previsto.
● ⑤ Referencias Retenidas por Bibliotecas Externas
Alguna “memoria oculta” es difícil de gestionar desde el código de la aplicación, lo que hace esencial el análisis mediante herramientas.
8-3. Puntos de Verificación para Detectar “Indicadores” de una Fuga de Memoria
Si observas lo siguiente, deberías sospechar fuertemente de una fuga de memoria:
- Solo la generación Old aumenta de forma constante
- El GC completo se vuelve más frecuente
- La memoria apenas disminuye incluso después de un GC completo
- El uso del heap crece con el tiempo de actividad
- Solo se producen fallas en producción después de largos periodos de ejecución
Estos conceptos son mucho más fáciles de comprender cuando se visualizan con herramientas.
8-4. Herramienta #1: Verificar Visualmente Fugas con VisualVM
VisualVM suele estar incluido con el JDK en algunas configuraciones y es muy accesible como primera herramienta.
● Qué Puedes Hacer con VisualVM
- Monitoreo en tiempo real del uso de memoria
- Confirmar el crecimiento de la generación Old
- Frecuencia de GC
- Monitoreo de hilos
- Capturar volcados de heap
● Cómo Capturar un Volcado de Heap
En VisualVM, abre la pestaña “Monitor” y haz clic en el botón “Heap Dump”.
Luego puedes pasar el volcado de heap capturado directamente a Eclipse MAT para un análisis más profundo.
8-5. Herramienta #2: Análisis Profundo con Eclipse MAT (Memory Analyzer Tool)
Si hay una herramienta estándar de la industria para el análisis de fugas de memoria en Java, es Eclipse MAT.
● Qué Puede Mostrarte MAT
- Qué objetos consumen más memoria
- Qué rutas de referencia mantienen vivos a los objetos
- Por qué los objetos no se liberan
- Inflación de colecciones
- Informes automáticos de “Leak Suspects” (Sospechosos de Fuga)
● Pasos Básicos de Análisis
- Abre el volcado de heap (*.hprof)
- Ejecuta el “Leak Suspects Report”
- Encuentra colecciones que retienen grandes cantidades de memoria
- Revisa el Dominator Tree para identificar los objetos “padre”
- Sigue la ruta de referencia (“Path to GC Root”)
8-6. Si Entiendes el Dominator Tree, el Análisis se Acelera Dramáticamente
El Dominator Tree te ayuda a identificar objetos que dominan (controlan) grandes porciones del uso de memoria.
Ejemplos incluyen:
- Un
ArrayListmasivo - Un
HashMapcon un número enorme de claves - Una caché que nunca se libera
- Un singleton mantenido por
static
Encontrar estos objetos puede reducir drásticamente el tiempo necesario para localizar la fuga.
8-7. Cómo Capturar un Volcado de Heap (Línea de Comandos)
También puedes capturar un volcado de heap usando jmap:
jmap -dump:format=b,file=heap.hprof <PID>
Puedes configurar la JVM para que genere automáticamente el volcado del heap cuando ocurra un OOM:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/heapdump.hprof
Esto es esencial para la investigación de incidentes en producción.
8-8. La Corrección Real de una Fuga Requiere Cambios de Código
Si existe una fuga, medidas como:
- Incrementar el tamaño del heap
- Afinar el GC
son solo medidas temporales de soporte vital.
En última instancia, necesitas cambios de diseño, tales como:
- Corregir la parte que mantiene referencias indefinidamente
- Revisar el diseño de las colecciones
- Evitar el uso excesivo de
static - Implementar la expulsión y limpieza de cachés

8-9. Cómo Diferenciar “Escasez de Heap” vs “Fuga de Memoria”
● En un Caso de Escasez de Heap
- El OOM ocurre rápidamente a medida que aumenta el volumen de datos
- Escala con la carga de trabajo
- Incrementar el heap estabiliza el sistema
● En un Caso de Fuga de Memoria
- El OOM ocurre después de un largo tiempo de actividad
- A medida que aumentan las solicitudes, el rendimiento empeora gradualmente
- La memoria apenas disminuye incluso después de un GC completo
- Incrementar el heap no lo soluciona
8-10. Resumen: Si el Ajuste del Heap No Resuelve el OOM, Sospecha una Fuga
Entre los problemas de “java heap space”, la causa raíz que más tiempo consume identificar suele ser una fuga de memoria.
Pero con VisualVM + Eclipse MAT, a menudo es posible descubrirla en minutos:
- Objetos que consumen más memoria
- Las referencias raíz que los mantienen vivos
- El origen de la inflación de colecciones
9. Problemas de “java heap space” en Docker / Kubernetes y Cómo Solucionarlos
answer.Aplicaciones Java modernas cada vez se ejecutan no solo en entornos locales, sino también en Docker y Kubernetes (K8s).
Sin embargo, dado que los entornos de contenedores usan un modelo de memoria diferente al del host, existen muchos puntos fáciles de malinterpretar para los desarrolladores Java, y los errores de “java heap space” o OOMKilled (terminación forzada del contenedor) pueden ocurrir con frecuencia.
Esta sección resume la gestión de memoria específica de contenedores y la configuración que debes conocer en operaciones reales.
9-1. Por qué los errores de Heap Space son tan comunes en contenedores
La razón es simple:
Java no siempre reconoce correctamente los límites de memoria del contenedor.
● Una concepción errónea común
“Como establecí un límite de memoria en Docker --memory=512m, Java debería ejecutarse dentro de los 512 MB.”
→ En la práctica, esa suposición puede ser incorrecta.
Al decidir el tamaño del heap, Java puede referirse a la memoria física del host en lugar de a los límites del contenedor.
Como resultado:
- Java asume “el host tiene mucha memoria”
- Intenta asignar un heap más grande
- Cuando supera el límite del contenedor, se ejecuta el OOM Killer y el proceso es terminado forzadamente
9-2. Mejoras en Java 8u191+ y Java 11+
A partir de ciertas actualizaciones de Java 8 y en Java 11+, se introdujo “UseContainerSupport”.
● Comportamiento en contenedores
- Puede reconocer los límites basados en cgroup
- Calcula automáticamente el tamaño del heap dentro de esos límites
Sin embargo, el comportamiento sigue variando según la versión, por lo que se recomienda una configuración explícita en producción.
9-3. Configuración explícita del tamaño del heap en contenedores (obligatoria)
● Patrón de inicio recomendado
docker run \
--memory=1g \
-e JAVA_OPTS="-Xms512m -Xmx800m" \
my-java-app
Puntos clave:
- Memoria del contenedor: 1 GB
- Heap de Java: mantenerlo dentro de 800 MB
- El resto se usa para pilas de hilos y memoria nativa
● Ejemplo incorrecto (muy común)
docker run --memory=1g my-java-app # no -Xmx
→ Java puede asignar el heap basándose en la memoria del host, y una vez que supera 1 GB, se produce OOMKilled.
9-4. Trampas de configuración de memoria en Kubernetes (K8s)
En Kubernetes, resources.limits.memory es crítico.
● Ejemplo de Pod
resources:
limits:
memory: "1024Mi"
requests:
memory: "512Mi"
En este caso, mantener -Xmx de Java alrededor de 800 MB a 900 MB suele ser más seguro.
● ¿Por qué establecerlo por debajo del límite?
Porque Java usa más que el heap:
- Memoria nativa
- Pilas de hilos (cientos de KB × número de hilos)
- Metaspace
- Sobrecarga de workers de GC
- Código JIT compilado
- Carga de bibliotecas
En conjunto, esto puede consumir fácilmente entre 100 y 300 MB.
En la práctica, una regla común es:
Si el límite = X, establece
-Xmxen aproximadamente X × 0.7 a 0.8 por seguridad.
9-5. Porcentaje automático del heap en Java 11+ (MaxRAMPercentage)
En Java 11, el tamaño del heap puede calcularse automáticamente mediante reglas como:
● Configuración predeterminada
-XX:MaxRAMPercentage=25
-XX:MinRAMPercentage=50
Lo que significa:
- El heap está limitado al 25 % de la memoria disponible
- En entornos pequeños, puede usar al menos 50 % como heap
● Configuración recomendada
En contenedores, suele ser más seguro establecer MaxRAMPercentage explícitamente:
JAVA_OPTS="-XX:MaxRAMPercentage=70"
9-6. Por qué OOMKilled ocurre tan a menudo en contenedores (patrón del mundo real)
Un patrón de producción común:
- Límite de memoria en K8s = 1 GB
- No se configura
-Xmx - Java referencia la memoria del host e intenta asignar un heap mayor a 1 GB
- El contenedor es terminado forzadamente → OOMKilled
Ten en cuenta que esto no es necesariamente un evento de java heap space (OutOfMemoryError); es una terminación a nivel de contenedor OOM.
9-7. Puntos de control específicos de contenedores usando logs de GC y métricas
En entornos de contenedores, presta especial atención a:
- Si los reinicios del pod están aumentando
- Si se registran eventos OOMKilled
- Si la generación Old sigue creciendo
- Si la recuperación de GC cae bruscamente en ciertos momentos
- Si la memoria nativa (no heap) se está agotando
Prometheus + Grafana hace que esto sea mucho más fácil de visualizar.
9-8. Resumen: “Configuraciones Explícitas” Son el Valor Predeterminado en Contenedores
--memorypor sí solo puede no permitir que Java calcule el heap correctamente- Siempre establezca
-Xmx - Deje margen para la memoria nativa y las pilas de hilos
- Establezca valores por debajo de los límites de memoria de Kubernetes
- En Java 11+, MaxRAMPercentage puede ser útil
10. Anti‑Patrones a Evitar (Código Defectuoso / Configuraciones Incorrectas)
El error “java heap space” ocurre no solo cuando el heap es realmente insuficiente, sino también cuando se emplean patrones de codificación peligrosos o configuraciones incorrectas.
Aquí resumimos los anti‑patrones más comunes que se observan frecuentemente en entornos reales.
10-1. Dejar que Colecciones Sin Límite Crezcan Indefinidamente
Uno de los problemas más frecuentes es la inflación de colecciones.
● Ejemplo Incorrecto: Añadir a una Lista Sin Ningún Límite
List<String> logs = new ArrayList<>();
while (true) {
logs.add(getMessage()); // ← grows forever
}
Con un tiempo de actividad prolongado, esto por sí solo puede llevar fácilmente a un OOM.
● Por Qué es Peligroso
- El GC no puede recuperar la memoria y la generación Old se inflama
- El Full GC se vuelve frecuente, aumentando la probabilidad de que la aplicación se congele
- Copiar una gran cantidad de objetos incrementa la carga de CPU
● Cómo Evitarlo
- Establezca un límite de tamaño (p. ej., una caché LRU)
- Realice limpiezas periódicas
- No retenga datos innecesariamente
10-2. Cargar Archivos o Datos Enormes de una Sola Vez
Este es un error común en procesamiento por lotes y del lado del servidor.
● Ejemplo Incorrecto: Leer un JSON Enorme de una Sola Vez
String json = Files.readString(Paths.get("large.json"));
Data d = mapper.readValue(json, Data.class);
● Qué Sale Mal
- Se retienen tanto la cadena original antes del parseo como los objetos resultantes en memoria
- Un archivo de 500 MB puede consumir más del doble de esa cantidad en memoria
- Se crean objetos intermedios adicionales, agotando el heap
● Cómo Evitarlo
- Utilice streaming (procesamiento secuencial)
- Lea en fragmentos en lugar de cargar todo de golpe
- No retenga el conjunto de datos completo en memoria
10-3. Continuar Manteniendo Datos en Variables estáticas
● Ejemplo Incorrecto
public class UserCache {
private static Map<String, User> cache = new HashMap<>();
}
● Por Qué es Peligroso
staticvive mientras la JVM esté en ejecución- Si se usa como caché, las entradas pueden nunca liberarse
- Las referencias permanecen, creando un caldo de cultivo para fugas de memoria
● Cómo Evitarlo
- Mantenga el uso de
statical mínimo - Utilice un framework de caché dedicado (p. ej., Caffeine)
- Defina TTL y un límite máximo de tamaño
10-4. Abusar de Stream / Lambda y Generar Listas Intermedias Enormes
La API Stream es cómoda, pero puede crear objetos intermedios internamente y ejercer presión sobre la memoria.
● Ejemplo Incorrecto (collect genera una lista intermedia masiva)
List<Item> result = items.stream()
.map(this::convert)
.collect(Collectors.toList());
● Cómo Evitarlo
- Procese secuencialmente con un bucle
for - Evite generar listas intermedias innecesarias
- Si el conjunto de datos es grande, reconsidere el uso de Stream en esa parte
10-5. Realizar Concatenaciones Masivas de Cadenas con el Operador +
Dado que los String son inmutables, cada concatenación crea un nuevo objeto String.
● Ejemplo Incorrecto
String result = "";
for (String s : list) {
result += s;
}
● Qué Está Mal
- Se crea un nuevo
Stringen cada iteración - Se generan una gran cantidad de instancias, presionando la memoria
● Cómo Evitarlo
StringBuilder sb = new StringBuilder();
for (String s : list) {
sb.append(s);
}
10-6. Crear Demasiadas Cachés y No Gestionarlas
● Ejemplos Incorrectos
- Almacenar respuestas de API en un
Mapindefinidamente - Cachear continuamente imágenes o datos de archivos
- No contar con un mecanismo de control como LRU
● Por Qué es Riesgoso
- La caché crece con el tiempo
- La memoria no recuperable aumenta
- Casi siempre se convertirá en un problema de producción
● Cómo Evitarlo
- Utilizar Caffeine / Guava Cache
- Establecer un tamaño máximo
- Configurar TTL (expiración)
10-7. Mantener registros o estadísticas en memoria continuamente
● Ejemplo incorrecto
List<String> debugLogs = new ArrayList<>();
debugLogs.add(message);
En producción, los registros deben escribirse en archivos o sistemas de registro. Mantenerlos en memoria es arriesgado.
10-8. No especificar -Xmx en contenedores Docker
Esto representa una gran parte de los incidentes modernos relacionados con el heap.
● Ejemplo incorrecto
docker run --memory=1g my-app
● Qué está mal
- Java puede dimensionar automáticamente el heap según la memoria del host
- Una vez que supera el límite del contenedor, se produce OOMKilled
● Cómo evitarlo
docker run --memory=1g -e JAVA_OPTS="-Xmx700m"
10-9. Sobreajustar la configuración del GC
Un ajuste incorrecto puede resultar contraproducente.
● Ejemplo incorrecto
-XX:MaxGCPauseMillis=10
-XX:G1HeapRegionSize=1m
Parámetros extremos pueden hacer que el GC sea demasiado agresivo o impedir que siga el ritmo.
● Cómo evitarlo
- En la mayoría de los casos, la configuración predeterminada es suficiente
- Sólo ajuste mínimamente cuando exista un problema específico y medido
10-10. Resumen: La mayoría de los anti‑patrones provienen de “Almacenar demasiado”
Lo que todos estos anti‑patrones tienen en común es:
“Acumular más objetos de los necesarios.”
- Colecciones sin límite
- Retención innecesaria
- Carga masiva
- Diseños con uso intensivo de estáticos
- Fuga de caché
- Explosión de objetos intermedios
Evitar estos por sí solo puede reducir drásticamente los errores de “java heap space”.
11. Ejemplos reales: Este código es peligroso (Patrones típicos de problemas de memoria)
Esta sección presenta ejemplos de código peligrosos que se encuentran frecuentemente en proyectos reales y que a menudo provocan errores de “java heap space”, y explica para cada uno:
“Por qué es peligroso” y “Cómo solucionarlo”.
En la práctica, estos patrones suelen ocurrir juntos, por lo que este capítulo es extremadamente útil para revisiones de código e investigaciones de incidentes.
11-1. Carga masiva de datos enormes
● Ejemplo incorrecto: Leer todas las líneas de un CSV enorme
List<String> lines = Files.readAllLines(Paths.get("big.csv"));
● Por qué es peligroso
- Cuanto mayor es el archivo, mayor es la presión de memoria
- Incluso un CSV de 100 MB puede consumir más del doble de memoria antes y después del análisis
- Retener registros masivos puede agotar la generación Old
● Mejora: Leer mediante Stream (procesamiento secuencial)
try (Stream<String> stream = Files.lines(Paths.get("big.csv"))) {
stream.forEach(line -> process(line));
}
→ Sólo una línea se mantiene en memoria a la vez, lo que lo hace muy seguro.
11-2. Patrón de hinchazón de colecciones
● Ejemplo incorrecto: Acumular continuamente objetos pesados en una lista
List<Order> orders = new ArrayList<>();
while (hasNext()) {
orders.add(fetchNextOrder());
}
● Por qué es peligroso
- Cada paso de crecimiento reasigna el array interno
- Si no necesitas conservar todo, es un desperdicio puro
- Las ejecuciones largas pueden consumir un enorme espacio en la generación Old
● Mejora: Procesar secuencialmente + por lotes cuando sea necesario
while (hasNext()) {
Order order = fetchNextOrder();
process(order); // process without retaining
}
O por lotes:
List<Order> batch = new ArrayList<>(1000);
while (hasNext()) {
batch.add(fetchNextOrder());
if (batch.size() == 1000) {
processBatch(batch);
batch.clear();
}
}
11-3. Generar demasiados objetos intermedios mediante la API Stream
● Ejemplo incorrecto: Listas intermedias repetidas mediante map → filter → collect
List<Data> result = list.stream()
.map(this::convert)
.filter(d -> d.isValid())
.collect(Collectors.toList());
● Por qué es peligroso
- Crea muchos objetos temporales internamente
- Especialmente arriesgado con listas enormes
- Cuanto más profunda es la cadena, mayor es el riesgo
● Mejora: Utilizar un bucle for o procesamiento secuencial
.«` List result = new ArrayList<>(); for (Item item : list) { Data d = convert(item); if (d.isValid()) { result.add(d); } }
## 11-4. Analizando JSON o XML de una Vez
### ● Ejemplo Incorrecto
String json = Files.readString(Paths.get(«large.json»)); Data data = mapper.readValue(json, Data.class);
### ● Por Qué es Peligroso
* Tanto la cadena JSON cruda como los objetos deserializados permanecen en memoria
* Con archivos de 100 MB, el heap puede llenarse al instante
* Problemas similares pueden ocurrir incluso al usar APIs de Stream, según el uso
### ● Mejora: Usar una API de Streaming
JsonFactory factory = new JsonFactory(); try (JsonParser parser = factory.createParser(new File(«large.json»))) { while (!parser.isClosed()) { JsonToken token = parser.nextToken(); // Process only when needed and do not retain data } }
## 11-5. Cargar Todas las Imágenes / Datos Binarios en Memoria
### ● Ejemplo Incorrecto
byte[] image = Files.readAllBytes(Paths.get(«large.png»));
### ● Por Qué es Peligroso
* Los datos binarios pueden ser grandes y “pesados” por naturaleza
* En aplicaciones de procesamiento de imágenes, esta es una causa principal de OOM
### ● Mejoras
* Utilizar buffering
* Procesar como un flujo sin retener todo el archivo en memoria
* La lectura masiva de logs con millones de líneas es igualmente peligrosa
## 11-6. Retención Infinita mediante Caché estática
### ● Ejemplo Incorrecto
private static final List
### ● Qué está Mal
* `sessions` no se liberará hasta que la JVM termine
* Crece con las conexiones y eventualmente produce OOM
### ● Mejoras
* Usar una caché con gestión de tamaño (Caffeine, Guava Cache, etc.)
* Gestionar claramente el ciclo de vida de la sesión
## 11-7. Uso Incorrecto de ThreadLocal
### ● Ejemplo Incorrecto
private static final ThreadLocal
ThreadLocal es útil, pero con pools de hilos puede mantener valores vivos y provocar fugas.
### ● Mejoras
* Mantener ThreadLocal de corta duración
* Evitar usarlo a menos que sea realmente necesario
* Llamar a `remove()` para limpiarlo
## 11-8. Crear Demasiadas Excepciones
Esto suele pasarse por alto, pero las Excepciones son **objetos muy pesados** debido a la generación del stack trace.
### ● Ejemplo Incorrecto
for (…) { try { doSomething(); } catch (Exception e) { // log only } }
→ Un exceso de excepciones puede presionar la memoria.
### ● Mejoras
* No usar excepciones para el flujo de control normal
* Rechazar entradas inválidas mediante validación
* Evitar lanzar excepciones a menos que sea necesario
## 11-9. Resumen: El Código Peligroso “Silenciosamente” Consume tu Heap
El tema común es:
**“estructuras que van apretando gradualmente el heap, una sobre otra.”**
* Carga masiva
* Colecciones infinitas
* Olvidar anular el registro/limpiar
* Creación de objetos intermedios
* Inundación de excepciones
* Retención estática
* Residuos de ThreadLocal
En todos los casos, el impacto se vuelve evidente durante ejecuciones prolongadas.
## 12. Mejores Prácticas de Gestión de Memoria en Java (Esenciales para Evitar Repeticiones)
Hasta ahora, hemos cubierto las causas de los errores “java heap space” y contramedidas como expansión del heap, mejoras de código, afinación del GC e investigación de fugas.
Esta sección resume **las mejores prácticas que previenen de forma fiable la recurrencia en entornos reales**.
Piénsalas como las reglas mínimas para mantener estables las aplicaciones Java.
## 12-1. Definir el Tamaño del Heap Explícitamente (Especialmente en Producción)
Ejecutar cargas de trabajo de producción con los valores por defecto es arriesgado.
### ● Mejores Prácticas
* Definir explícitamente `-Xms` y `-Xmx`
* No ejecutar producción con los valores por defecto
* Mantener los tamaños del heap consistentes entre desarrollo y producción (evitar diferencias inesperadas)
Ejemplo:
-Xms1g -Xmx1g
En Docker / Kubernetes, debes establecer el heap más pequeño para que coincida con los límites del contenedor.
## 12-2. Monitorear Adecuadamente (GC, Uso de Memoria, OOM)
Los problemas de heap a menudo son prevenibles si se detectan señales de advertencia tempranas.
### ● Qué monitorear
* Uso de la generación antigua
* Tendencias de crecimiento de la generación joven
* Frecuencia de Full GC
* Tiempo de pausa de GC
* Eventos de OOMKilled en contenedores
* Conteo de reinicios de Pod (K8s)
### ● Herramientas recomendadas
* VisualVM
* JDK Mission Control
* Prometheus + Grafana
* Métricas del proveedor de nube (p. ej., CloudWatch)
Un aumento gradual en el uso de memoria durante tiempos de ejecución largos es un signo clásico de fuga de memoria.
## 12-3. Usa “Caches controlados”
La fuga de caché es una de las causas más comunes de OOM en producción.
### ● Mejores prácticas
* Usa Caffeine / Guava Cache
* Siempre configura TTL (expiración)
* Establece un tamaño máximo (p. ej., 1.000 entradas)
* Evita las cachés estáticas tanto como sea posible
Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .build();
## 12-4. Ten cuidado con el uso excesivo de la API Stream y Lambdas
Para conjuntos de datos grandes, encadenar operaciones Stream aumenta los objetos intermedios.
### ● Mejores prácticas
* No encadenes map/filter/collect más de lo necesario
* Procesa conjuntos de datos enormes de manera secuencial con un bucle for
* Al usar collect, sé consciente del volumen de datos
Los Streams son convenientes, pero no siempre son amigables con la memoria.
## 12-5. Cambia archivos enormes / datos enormes a Streaming
El procesamiento en masa es una causa raíz principal de problemas de heap.
### ● Mejores prácticas
* CSV → `Files.lines()`
* JSON → Jackson Streaming
* DB → paginación
* API → obtención en chunks (cursor/paginación)
Si aplicas “no cargues todo en memoria”, muchos problemas de espacio de heap desaparecen.
## 12-6. Trata ThreadLocal con cuidado
ThreadLocal es poderoso, pero un mal uso puede causar fugas de memoria graves.
### ● Mejores prácticas
* Sé especialmente cuidadoso cuando se combine con pools de hilos
* Llama a `remove()` después del uso
* No almacenes datos de larga duración
* Evita ThreadLocal estático siempre que sea posible
## 12-7. Captura volcados de heap periódicamente para detectar fugas tempranas
Para sistemas de larga ejecución (aplicaciones web, sistemas de lotes, IoT), capturar volcados de heap regularmente y compararlos ayuda a detectar signos tempranos de fugas.
### ● Opciones
* VisualVM
* jmap
* `-XX:+HeapDumpOnOutOfMemoryError`
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/heapdump.hprof
El volcado automático en OOM es una configuración indispensable en producción.
## 12-8. Mantén la sintonización de GC mínima
La idea de que “sintonizar GC impulsará automáticamente el rendimiento” puede ser peligrosa.
### ● Mejores prácticas
* Comienza con **configuraciones predeterminadas**
* Realiza cambios mínimos solo cuando existe un problema medido
* Usa G1GC como la elección predeterminada
* En muchos casos, aumentar el heap es más efectivo que la micro-sintonización
## 12-9. Considera dividir la arquitectura
Si los volúmenes de datos se vuelven demasiado grandes o la aplicación se vuelve demasiado monolítica y demanda un heap masivo, podrías necesitar mejoras arquitectónicas:
* Microservicios
* División del procesamiento de datos en lotes
* Desacoplamiento con colas de mensajes (Kafka, etc.)
* Procesamiento distribuido (Spark, etc.)
Si “no importa cuánto heap agregues, nunca es suficiente”, sospecha de un problema arquitectónico.
## 12-10. Resumen: La gestión de memoria en Java se trata de optimización en capas
Los problemas de espacio de heap rara vez se resuelven con una sola configuración o una sola corrección de código.
### ● Puntos clave
* Siempre establece el heap explícitamente
* El monitoreo importa más
* Nunca permitas hinchazón de colecciones
* Usa streaming para datos grandes
* Gestiona las cachés correctamente
* Usa ThreadLocal con cuidado
* Analiza fugas con herramientas cuando sea necesario
* Los contenedores requieren una mentalidad diferente
Siguiendo estos puntos prevenirás la mayoría de los errores “java heap space” con alta certeza.
## 13. Resumen: Puntos clave para prevenir errores “java heap space”
En este artículo, cubrimos los errores “java heap space” desde las causas raíz hasta la mitigación y prevención de recurrencia.
Aquí organizamos los esenciales como un resumen práctico.
## 13-1. El problema real no es “El heap es demasiado pequeño” sino “¿Por qué se está agotando?”
“java heap space” no es solo una simple escasez de memoria.
### ● La causa raíz es típicamente una de las siguientes
* **El tamaño del heap es demasiado pequeño** (configuración insuficiente)
* **Procesamiento masivo de datos enormes** (problema de diseño)
* **Hinchazón de colecciones** (falta de eliminación/diseño)
* **Fuga de memoria** (referencias pendientes)
* **Mala configuración en contenedores** (específica de Docker/K8s)
Comienza con: “¿Por qué se agotó el heap?”
## 13-2. Primeros pasos para investigar
### ① Confirmar el tamaño del heap
→ Establecer explícitamente `-Xms` / `-Xmx`
### ② Entender las restricciones de memoria en tiempo de ejecución
→ En Docker/Kubernetes, alinear límites y tamaño del heap
→ También verificar `-XX:MaxRAMPercentage`
### ③ Capturar e inspeccionar registros de GC
→ El crecimiento de old-gen y GC completos frecuentes son señales de alerta
### ④ Capturar y analizar volcados de heap
→ Usar VisualVM / MAT para establecer evidencia de fugas
## 13-3. Patrones de alto riesgo comunes en producción
Como se muestra a lo largo de este artículo, los siguientes patrones frecuentemente llevan a incidentes:
* **Procesamiento masivo de archivos enormes**
* **Agregar a List/Map sin un límite**
* **Caché descontrolado**
* **Acumular datos en static**
* **Objetos intermedios explosivos mediante cadenas de Stream**
* **Mal uso de ThreadLocal**
* **No establecer -Xmx en Docker**
Si ves estos en el código o configuraciones, investiga primero.
## 13-4. Las correcciones fundamentales se tratan de diseño de sistema y procesamiento de datos
### ● Qué revisar a nivel de sistema
* Cambiar el manejo de datos grandes a **procesamiento en streaming**
* Usar cachés con **TTL, límites de tamaño y evicción**
* Realizar **monitoreo regular de memoria** para aplicaciones de larga duración
* Analizar señales tempranas de fugas con herramientas
### ● Si aún es difícil
* Separar procesamiento batch vs en línea
* Microservicios
* Adoptar plataformas de procesamiento distribuido (Spark, Flink, etc.)
Pueden requerirse mejoras arquitectónicas.
## 13-5. Los tres mensajes más importantes
Si solo recuerdas tres cosas:
### ✔ Siempre establece el heap explícitamente
### ✔ Nunca proceses datos enormes en masa
### ✔ No puedes confirmar fugas sin volcados de heap
Solo estos tres pueden reducir en gran medida los incidentes críticos en producción causados por “java heap space.”
## 13-6. La gestión de memoria en Java es una habilidad que crea una ventaja real
La gestión de memoria en Java puede parecer difícil, pero si la entiendes:
* La investigación de incidentes se vuelve dramáticamente más rápida
* Los sistemas de alta carga se pueden ejecutar de manera estable
* La sintonización de rendimiento se vuelve más precisa
* Te conviertes en un ingeniero que entiende tanto la aplicación como la infraestructura
No es una exageración decir que la calidad del sistema es proporcional al entendimiento de la memoria.
## 14. Preguntas frecuentes
Finalmente, aquí hay una sección práctica de Q&A que cubre preguntas comunes que la gente busca alrededor de “java heap space.”
Esto complementa el artículo y ayuda a capturar una gama más amplia de intenciones de usuario.
## Q1. ¿Cuál es la diferencia entre `java.lang.OutOfMemoryError: Java heap space` y `GC overhead limit exceeded`?
### ● java heap space
* Ocurre cuando el heap está **físicamente agotado**
* A menudo causado por datos enormes, hinchazón de colecciones o configuraciones insuficientes
### ● GC overhead limit exceeded
* GC está trabajando duro pero **recuperando casi nada**
* Una señal de que GC no puede recuperar debido a demasiados objetos vivos
* A menudo sugiere una fuga de memoria o referencias pendientes
Un modelo mental útil:
**heap space = ya se cruzó el límite**,
**GC overhead = justo antes del límite**.
## Q2. Si simplemente aumento el heap, ¿se resolverá?
### ✔ Puede ayudar temporalmente
### ✘ No corrige la causa raíz
* Si el heap es genuinamente demasiado pequeño para tu carga de trabajo → ayuda
* Si las colecciones o fugas son la causa → se repetirá
Si la causa es una fuga, duplicar el heap solo retrasa el siguiente OOM.
## Q3. ¿Cuánto puedo aumentar el heap de Java?
### ● Típicamente: 50%–70% de la memoria física
Porque debes reservar memoria para:
* Memoria nativa
* Pilas de hilos
* Metaspace
* Trabajadores de GC
* Procesos del SO
Especialmente en Docker/K8s, es una práctica común establecer:
**-Xmx = 70%–80% del límite del contenedor**.
## Q4. ¿Por qué Java se OOMKilled en contenedores (Docker/K8s)?
.### ● En muchos casos, porque no se establece `-Xmx`
Docker no siempre pasa los límites del contenedor a Java de forma limpia, por lo que Java dimensiona el heap basándose en la memoria del host → supera el límite → OOMKilled.
### ✔ Solución
docker run –memory=1g -e JAVA_OPTS=»-Xmx800m»
## Q5. ¿Existe una forma fácil de saber si es una fuga de memoria?
### ✔ Si esto es cierto, es muy probable que sea una fuga
* El uso del heap sigue aumentando con el tiempo de actividad
* La memoria apenas disminuye incluso después de un Full GC
* La generación old crece en un patrón de “escalones”
* OOM ocurre después de horas o días
* Las ejecuciones cortas se ven bien
Sin embargo, la confirmación final requiere **análisis de volcado de heap (Eclipse MAT)**.
## Q6. La configuración de heap en Eclipse / IntelliJ no se aplica
### ● Causas comunes
* No editaste la Configuración de Ejecución
* Las configuraciones predeterminadas del IDE tienen prioridad
* Otro script de inicio con `JAVA_OPTS` sobrescribe tus ajustes
* Olvidaste reiniciar el proceso
Las configuraciones del IDE difieren, así que siempre verifica el campo “VM options” en la Configuración de Ejecución/Depuración.
## Q7. ¿Es cierto que Spring Boot consume mucha memoria?
Sí. Spring Boot suele consumir más memoria debido a:
* Autoconfiguración
* Muchos Beans
* Carga de clases en JARs “fat”
* Servidor web embebido (Tomcat, etc.)
En comparación con un programa Java simple, puede usar **aproximadamente 200–300 MB adicionales** en algunos casos.
## Q8. ¿Qué recolector de basura (GC) debería usar?
En la mayoría de los casos, **G1GC** es la opción predeterminada segura.
### ● Recomendaciones según la carga de trabajo
* Aplicaciones web → G1GC
* Jobs por lotes con alto rendimiento → Parallel GC
* Necesidades de latencia ultra‑baja → ZGC / Shenandoah
Sin una razón fuerte, elige G1GC.
## Q9. ¿Cómo debo manejar el heap en entornos serverless (Cloud Run / Lambda)?
Los entornos serverless tienen límites de memoria estrictos, por lo que debes **configurar el heap explícitamente**.
Ejemplo (Java 11):
-XX:MaxRAMPercentage=70 «`
También ten en cuenta que la memoria puede aumentar durante los arranques en frío, así que deja margen en la configuración del heap.
Q10. ¿Cómo puedo evitar que los problemas de heap en Java se repitan?
Si sigues estrictamente estas tres reglas, la recurrencia disminuye drásticamente:
✔ Configura el heap explícitamente
✔ Procesa grandes volúmenes de datos mediante streaming
✔ Revisa regularmente los logs de GC y los volcados de heap
Resumen: Usa el FAQ para eliminar dudas y aplicar contramedidas prácticas de memoria
Este FAQ cubre preguntas comunes basadas en búsquedas sobre “java heap space” con respuestas prácticas.
Junto con el artículo principal, debería ayudarte a manejar con soltura los problemas de memoria en Java y mejorar significativamente la estabilidad del sistema en producción.


