Layer 8Ehrlicher Tech-Support
SoftwareSchweregrad: Systemzerstö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.

Aufwand:
ca. 25 Min.
Gilt für:
JDK 21, JDK 25

Das Symptom

Die Anwendung läuft, oft tagelang, und bricht dann mit java.lang.OutOfMemoryError: Metaspace ab. Die Überwachung zeigt gleichzeitig einen Heap, der bequem im grünen Bereich liegt. Besonders gern trifft es Anwendungsserver nach dem dritten oder vierten Redeploy derselben Anwendung, ohne dass zwischendurch jemand Code geändert hätte.

Wer daraufhin -Xmx erhöht, erlebt das Gegenteil von Besserung: Der Fehler kommt wieder, manchmal früher.

Die „Layer 8“-Wahrheit

Der Name der Ausnahme ist identisch, der Speicherbereich ist es nicht. OutOfMemoryError ist ein Sammelbegriff, und die Detail-Meldung dahinter ist der eigentliche Fehlercode. Metaspace bezeichnet den Bereich, in dem die JVM ihre interne Darstellung von Klassen ablegt – nicht Objekte, sondern die Bauanleitungen für Objekte. Dieser Bereich liegt laut Oracle im nativen Speicher, nicht im Heap. Am Heap-Regler zu drehen ist deshalb ungefähr so wirksam, wie den Tank zu vergrößern, weil die Bremsflüssigkeit fehlt.

Interessanter wird es beim zweiten Ratschlag aus derselben Quelle: Den Heap verkleinern, um Metaspace Platz zu machen. Beide werden aus demselben Adressraum bedient. Das ist kein Trick, sondern schlichte Arithmetik – und Oracle hängt die Einschränkung gleich an, dass sich der Handel nur bei reichlich freiem Heap lohnt.

Bleibt die unangenehme Variante. Steigt der Metaspace-Verbrauch mit jedem Redeploy monoton an, ist keine Grenze zu klein, sondern etwas hält alte Klassenlader fest. Solange auch nur eine Referenz auf einen ausrangierten Lader existiert, darf die JVM dessen gesamte Klassen-Metadaten nicht freigeben – und ein Anwendungsserver erzeugt bei jedem Deploy einen neuen. Eine höhere Grenze verlängert dann nur den Abstand zwischen den Abstürzen und verlängert vor allem den Heap-Dump, den am Ende doch jemand lesen muss.

Die echte, verifizierte Lösung

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

  1. 1.

    Die Detail-Meldung wirklich lesen

    Die JVM hängt an jeden OutOfMemoryError eine Detail-Meldung an, und die entscheidet über alles Weitere. Steht dort Java heap space, ist der Heap gemeint; steht dort Metaspace, sind es die Klassen-Metadaten im nativen Speicher. Zwei völlig verschiedene Baustellen mit demselben Ausnahmenamen – wer das verwechselt, dreht 20 Minuten lang am falschen Regler.

  2. 2.

    Prüfen, ob überhaupt eine Grenze gesetzt ist

    Oracles Handlungsanweisung beginnt mit der Bedingung, dass MaxMetaspaceSize auf der Kommandozeile angegeben wurde. Ist sie es nicht, steht der Wert praktisch auf unbegrenzt, und der Fehler bedeutet dann etwas anderes: Der native Speicher der Maschine oder des Containers ist zuerst ausgegangen. Erst den Ist-Zustand ansehen, dann handeln.

    java -XX:+PrintFlagsFinal -version | grep -i MetaspaceSize
    
  3. 3.

    Beim nächsten Absturz einen Speicherabzug erzwingen

    Raten hilft hier nicht weiter – man braucht die Liste der geladenen Klassenlader. Die JVM schreibt auf Wunsch beim Auftreten des Fehlers automatisch einen Heap-Dump. Ohne HeapDumpPath landet die Datei im aktuellen Arbeitsverzeichnis und heißt java_pid<pid>.hprof.

    java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/java -jar app.jar
    
  4. 4.

    Die Grenze anheben – wenn sie gesetzt war

    Wurde MaxMetaspaceSize explizit gesetzt und ist der Bedarf schlicht gewachsen, ist die Anhebung die von Oracle genannte Maßnahme. Das ist die richtige Antwort auf eine zu eng gezogene Grenze, aber die falsche auf ein Leck: Wenn der Verbrauch monoton steigt, kauft eine höhere Grenze nur Zeit.

    java -XX:MaxMetaspaceSize=512m -jar app.jar
    
  5. 5.

    Den Heap verkleinern, statt ihn zu vergrößern

    Die zweite Maßnahme aus der Oracle-Dokumentation klingt widersinnig und ist es nicht: Metaspace wird aus demselben Adressraum wie der Java-Heap bedient, ein kleinerer Heap gibt also Platz frei. Oracle schränkt das ausdrücklich ein – der Handel lohnt sich nur, wenn im Heap tatsächlich reichlich Luft ist.

Häufige Fragen (FAQ)

?Ich habe den Heap mit -Xmx verdoppelt und der Fehler kommt trotzdem. Warum?

Weil der Heap nicht das Problem ist. Klassen-Metadaten legt die JVM im Metaspace ab, und der liegt im nativen Speicher, nicht im Heap. Oracle beschreibt sogar den umgekehrten Weg: Metaspace wird aus demselben Adressraum wie der Java-Heap bedient, ein kleinerer Heap schafft also mehr Platz für Metaspace. Ein größerer Heap verschärft die Lage.

?Bei jedem Redeploy im Anwendungsserver kommt der Fehler ein Stück früher. Ist das normal?

Normal nicht, aber typisch. Beim Redeploy lädt der Server dieselben Klassen ein weiteres Mal über einen neuen Klassenlader. Bleibt eine Referenz auf den alten Lader hängen, kann die JVM dessen Metadaten nicht freigeben, und der Metaspace wächst mit jedem Zyklus. Ein Neustart des Prozesses schiebt das Problem nur auf.

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.

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ästigUnsupportedClassVersionError

Java: UnsupportedClassVersionError

Das Programm wurde mit einem neueren Java kompiliert, als es ausführt? Diese Meldung nennt sogar die Versionsnummern — man muss sie nur übersetzen.