Layer 8Ehrlicher Tech-Support
SoftwareSchweregrad: LästigOutOfMemoryError: Java heap space

Java im Container: 4 GB Limit, die JVM nimmt sich 1 GB

Container mit 4 GB, Java bricht bei 1 GB mit OutOfMemoryError ab: Ohne -Xmx nimmt die JVM standardmäßig 25 Prozent des erkannten Speichers als Heap.

Aufwand:
ca. 20 Min.
Voraussetzung:
java-Kommando mit -XX:+PrintFlagsFinal, Container-Laufzeit mit Speicherlimit, Java SE 21 Tools Reference, Beschreibung des java-Kommandos
Gilt für:
Java SE 21 (Beschreibung des java-Kommandos in der am 2026-10-15 abrufbaren Fassung), Linux-Container; die Containererkennung ist laut Quelle auf Linux beschränkt

Das Symptom

Der Administrator gibt dem Container 4 GB Speicher und wundert sich, dass die Java-Anwendung mit OutOfMemoryError: Java heap space abbricht, obwohl der Container laut Monitoring nie mehr als gut 1 GB belegt. Er hebt das Containerlimit auf 8 GB, der Fehler bleibt. Der umgekehrte Irrtum kommt genauso vor: Wer -Xmx auf die volle Containergröße setzt, weil Java ja den ganzen Speicher haben soll, bekommt einen vom Kernel beendeten Container, weil neben dem Heap noch Metaspace, Threads und nativer Speicher Platz brauchen.

Die „Layer 8“-Wahrheit

Die Beschreibung des java-Kommandos erklärt beides. Erstens erkennt die JVM unter Linux Container-Grenzen automatisch. Zur Option -XX:-UseContainerSupport heißt es: „Linux only: The VM now provides automatic container detection support, which allows the VM to determine the amount of memory and number of processors that are available to a Java process running in docker containers. It uses this information to allocate system resources. The default for this flag is true, and container support is enabled by default.“ Das Limit wird also gelesen.

Zweitens nimmt sie sich davon nicht alles. Zu -XX:MaxRAMPercentage steht dort: „Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the -XX:MaxRAM option. The default value is 25 percent.“ Bei 4 GB Containerlimit sind das rund 1 GB Heap, und genau dort schlägt der OutOfMemoryError zu, während der Container aus Sicht des Orchestrators halb leer ist. Das Limit anzuheben ändert an der Rechnung nichts; es vergrößert nur das Viertel.

Die andere Richtung hat ihre eigene Grenze. -Xmx „Specifies the maximum size (in bytes) of the heap“, und der Heap ist nur ein Teil dessen, was der Prozess belegt. Wer ihn auf das Containerlimit setzt, lässt dem Rest keinen Platz, und dann beendet nicht die JVM die Anwendung mit einem Stacktrace, sondern der Kernel den Prozess ohne einen. Wer mehr Heap will, setzt -XX:MaxRAMPercentage auf etwa 60 bis 75 oder -Xmx explizit und lässt Luft für den Nicht-Heap-Anteil. -Xlog:os+container=trace zeigt beim Start, was die JVM vom Container erkannt hat.

Beide Irrtümer haben dieselbe Wurzel: Das Containerlimit wird für die Heap-Größe gehalten. Es ist weder das, was die JVM nimmt, noch das, was sie nehmen dürfte. Dazwischen liegt ein Prozentsatz, der in der Dokumentation steht und in kaum einem Deployment.

Wer das einmal mit -XX:+PrintFlagsFinal im Container gesehen hat, vergisst es nicht mehr: MaxHeapSize zeigt in Bytes, was die JVM sich genommen hat, und die Zahl ist ein Viertel dessen, was der Orchestrator dem Container zugesteht. Alles Weitere ist eine Entscheidung über den Prozentsatz, und die trifft man besser in der Startkonfiguration als in der Nacht, in der der Container zum dritten Mal neu startet.

Die echte, verifizierte Lösung

1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:

  1. 1.

    Nachsehen, was die JVM als Heap-Maximum gewählt hat

    Im Container java mit -XX:+PrintFlagsFinal starten und nach MaxHeapSize und MaxRAMPercentage filtern. MaxHeapSize zeigt in Bytes, was die JVM tatsächlich als Obergrenze gewählt hat; steht dort ein Viertel des Containerlimits, ist die Ursache gefunden, bevor irgendeine Einstellung geändert wird.

    java -XX:+PrintFlagsFinal -version | grep -E 'MaxHeapSize|MaxRAMPercentage'
    
  2. 2.

    MaxRAMPercentage anheben oder -Xmx setzen

    Statt das Containerlimit zu vergrößern, den Anteil ändern: -XX:MaxRAMPercentage mit einem Wert zwischen 60 und 75, oder ein explizites -Xmx. Der Prozentwert hat den Vorteil, dass er mit dem Containerlimit mitwächst, wenn der Orchestrator es ändert; ein festes -Xmx muss bei jeder Änderung nachgezogen werden.

    java -XX:MaxRAMPercentage=70 -jar app.jar
    
  3. 3.

    Zwischen Heap und Containerlimit Luft lassen

    Der Abstand zwischen Heap-Maximum und Containerlimit ist der Platz für Metaspace, Threads, Codecache und nativen Speicher. Wird er zu klein, beendet der Kernel den Prozess, und das sieht im Orchestrator anders aus als ein OutOfMemoryError: Der Container ist einfach weg, ohne Stacktrace. Wie viel Luft nötig ist, hängt von der Anwendung ab; beobachten, nicht raten.

  4. 4.

    Prüfen, ob die JVM das Limit überhaupt erkennt

    Bei sehr alten Laufzeiten oder ungewöhnlichen Container-Umgebungen die Erkennung kontrollieren. Die Protokollierung des Containerbereichs zeigt beim Start, welches Speicherlimit die JVM gelesen hat; steht dort der Speicher des Hosts statt des Containers, ist die Erkennung ausgefallen, und 25 Prozent beziehen sich auf die falsche Zahl.

    java -Xlog:os+container=trace -version
    

Häufige Fragen (FAQ)

?Der Container hat 4 GB. Warum bricht Java bei gut 1 GB mit OutOfMemoryError ab?

Weil die JVM ohne -Xmx nur einen Teil des erkannten Speichers als Heap verwendet. Die Beschreibung des java-Kommandos sagt zu -XX:MaxRAMPercentage: „Sets the maximum amount of memory that the JVM may use for the Java heap before applying ergonomics heuristics as a percentage of the maximum amount determined as described in the -XX:MaxRAM option. The default value is 25 percent.“ Bei 4 GB Containerlimit sind das rund 1 GB Heap, und genau dort schlägt der OutOfMemoryError zu, während der Container aus Sicht des Orchestrators halb leer ist.

?Erkennt die JVM das Containerlimit überhaupt?

Ja, auf Linux seit Längerem automatisch. Zur Option -XX:-UseContainerSupport heißt es: „Linux only: The VM now provides automatic container detection support, which allows the VM to determine the amount of memory and number of processors that are available to a Java process running in docker containers. It uses this information to allocate system resources. The default for this flag is true, and container support is enabled by default.“ Erkannt wird das Limit also; nur nimmt sich die JVM davon standardmäßig ein Viertel für den Heap.

?Dann setze ich -Xmx auf die volle Containergröße?

Nein. -Xmx „Specifies the maximum size (in bytes) of the heap“, und der Heap ist nicht der ganze Speicherbedarf der JVM. Metaspace, Threads, Codecache und nativer Speicher kommen dazu. Wer den Heap auf das Containerlimit setzt, bekommt statt des OutOfMemoryError einen vom Kernel beendeten Prozess, im Orchestrator als OOMKilled sichtbar. Der Heap bekommt einen großen Anteil, aber nicht alles.

?Warum hilft es nicht, das Containerlimit auf 8 GB zu heben?

Weil 25 Prozent von 8 GB zwar 2 GB sind, die Rechnung aber dieselbe bleibt: Die JVM nimmt sich weiterhin ein Viertel, drei Viertel liegen brach, und sobald die Anwendung 2 GB braucht, steht derselbe Fehler wieder da. Das Limit ist nicht das Problem, der Prozentsatz ist es. Wer den Heap vergrößern will, ändert den Prozentsatz oder setzt -Xmx; das Containerlimit anzuheben, ist die teuerste Art, ein Viertel zu vergrößern.

Belege

Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.

Zuletzt geprüft am . Dokumentation altert – ein Datum daneben ist der einzige ehrliche Umgang damit.

Auch zu finden unter

Verwandte Fehler

SWSystemzerstörendjava.lang.OutOfMemoryError: Java heap space

OutOfMemoryError: Java heap space

Der Java-Prozess läuft gegen sein Speicherlimit. Mehr Heap zu geben ist oft nur ein Pflaster – meist steckt ein Speicherleck dahinter.

SWSystemzerstörendjava.lang.OutOfMemoryError: Metaspace

java.lang.OutOfMemoryError: Metaspace

Der Heap ist halb leer, die JVM stirbt trotzdem. Metaspace liegt im nativen Speicher und hat eine eigene Grenze – ein größerer Heap hilft nicht.

SWLästig

jcmd findet keine Java-Prozesse

jcmd -l listet nichts oder nur sich selbst, obwohl die JVM läuft. Schuld ist fast immer der falsche Benutzer – und sudo macht es schlimmer, nicht besser.

SWLästigRestarting (1)

Docker-Container startet endlos neu

Der Container hängt in einer Neustartschleife. Der Hauptprozess beendet sich – die Logs des abgestürzten Laufs verraten, warum.

SWLästigexec format error

Docker: exec format error

Der Container startet nicht und meldet „exec format error“? Fast immer passt die Prozessorarchitektur des Images nicht zum Host — ARM gegen x86.