Minecraft-Server: "java.lang.OutOfMemoryError: Java heap space" beheben
Die Meldung java.lang.OutOfMemoryError: Java heap space bedeutet nicht automatisch zu wenig RAM. Wie Sie Xmx und Xms richtig dimensionieren, Speicherlecks finden und Swap sinnvoll einsetzen.
Der Server läuft seit drei Stunden, dann friert er ein, die Tickrate fällt auf 2 und in der Konsole steht:
[Server thread/ERROR]: Encountered an unexpected exception
java.lang.OutOfMemoryError: Java heap space
at java.base/java.util.Arrays.copyOf(Arrays.java:3537)
at it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap.rehash(...)
Der erste Reflex ist fast immer derselbe: mehr RAM zuweisen. In etwa der Hälfte der Fälle ist das genau die falsche Reaktion, und in einem Teil davon macht es die Sache messbar schlimmer. Dieser Artikel zeigt, was die Meldung wirklich bedeutet, wie -Xmx und -Xms sauber dimensioniert werden, wie Sie ein Speicherleck von echtem Speichermangel unterscheiden und woran Sie erkennen, dass der Fix gehalten hat.
Was die Meldung genau bedeutet, und was nicht
Der Java-Heap ist der Bereich, in dem die JVM Objekte ablegt: geladene Chunks, Entities, Inventare, Plugin-Daten. Seine Obergrenze legen Sie mit -Xmx fest. Ein OutOfMemoryError: Java heap space heißt: Die JVM wollte ein Objekt anlegen, der Heap war voll, und die Garbage Collection konnte nicht genug freiräumen. Über den freien Speicher des Betriebssystems sagt das nichts aus. Ein Server mit 64 GB RAM wirft die Meldung genauso zuverlässig, wenn -Xmx2G gesetzt ist und die Welt 4 GB braucht.
Genau davon zu unterscheiden sind zwei andere Ausfälle, die gern verwechselt werden:
- Der Prozess verschwindet ohne Stacktrace, im Log steht nur
Killedoder im JournalMain process exited, code=killed, status=9/KILL. Das war der Kernel, nicht die JVM. Der Linux-OOM-Killer hat zugeschlagen, weil der gesamte Speicher des Systems aufgebraucht war. Prüfen lässt sich das mitdmesg | grep -i "out of memory", dort steht dann eine Zeile wieOut of memory: Killed process 1337 (java). - Java startet gar nicht und meldet
Error occurred during initialization of VM / Could not reserve enough space for object heap. Dann ist-Xmxgrößer als das, was das System überhaupt hergeben kann.
Diese Unterscheidung ist der wichtigste Schritt. Bei Variante eins zu wenig Heap, bei Variante zwei und drei zu viel. Wer die Fälle verwechselt, dreht an der falschen Schraube.
Erst messen, dann zuweisen
Bevor irgendein Wert geändert wird, brauchen Sie zwei Zahlen: den tatsächlich verfügbaren Speicher und den aktuellen Verbrauch.
free -h
cat /proc/meminfo | grep -E 'MemTotal|MemAvailable|SwapTotal'
Entscheidend ist MemAvailable, nicht free. Linux nutzt ungenutzten RAM als Dateicache, "free" ist deshalb fast immer klein und fast immer irrelevant.
Den echten Verbrauch des Servers sehen Sie so:
ps -o pid,rss,cmd -C java
Der RSS-Wert steht in Kilobyte und ist der Speicher, den der Prozess im RAM belegt. Er ist immer größer als -Xmx, und das ist der Punkt, an dem die meisten Anleitungen aufhören.
Warum "so viel wie möglich" garantiert schiefgeht
Die JVM braucht neben dem Heap noch eine ganze Reihe weiterer Speicherbereiche, die von -Xmx überhaupt nicht erfasst werden:
- Metaspace: die geladenen Klassen. Bei einem Modpack mit 300 Mods sind das schnell 300 bis 600 MB.
- Thread-Stacks: jeder Thread bekommt rund 1 MB. Chunk-Worker, Netty-Threads, Plugin-Scheduler, das summiert sich auf 100 bis 300 MB.
- Direct Buffers: Netty wickelt den kompletten Netzwerkverkehr über Speicher außerhalb des Heaps ab. Bei vielen gleichzeitigen Spielern sind mehrere hundert Megabyte normal.
- Code-Cache und GC-Strukturen: der JIT-Compiler und die Buchhaltung von G1 selbst kosten grob 5 bis 10 Prozent des Heaps.
Als belastbare Faustregel gilt: Rechnen Sie mit Xmx plus 1 bis 1,5 GB für die JVM, plus mindestens 512 MB für das Betriebssystem. Bei einem Modpack mit vielen Mods eher Xmx plus 2 GB.
Auf einem Server mit 8 GB RAM bedeutet das -Xmx6G und nicht -Xmx8G. Wer 8 zuweist, bekommt keinen Heap-Fehler mehr, sondern etwas Schlimmeres: einen Prozess, der ohne Vorwarnung vom Kernel abgeschossen wird, mitten im Speichern der Welt. Der Heap-Fehler ist ein sauberer, dokumentierter Ausfall. Der OOM-Kill kann korrupte Region-Dateien hinterlassen.
Es gibt noch einen zweiten Grund gegen maximale Zuweisung: Ein zu großer Heap macht die Garbage Collection langsamer. G1 muss mehr Speicher durchsuchen, die Pausen bei einem Mixed GC werden länger, und aus einem gelegentlichen Ruckler wird ein spürbarer Freeze. Oberhalb von etwa 12 GB kippt das Verhältnis bei Minecraft in der Regel ins Negative. Wer mehr braucht, sollte die Welt aufteilen statt den Heap zu vergrößern.
Faustregeln nach Spielerzahl und Modpack
Diese Werte sind Startpunkte, keine Naturgesetze. Sie gehen von einer normal großen Welt und aktivem Pregenerating aus.
| Servertyp | Spieler | Xmx | RAM im System |
|---|---|---|---|
| Vanilla oder Paper, ohne Plugins | bis 10 | 2G | 4 GB |
| Paper mit 15 bis 30 Plugins | 10 bis 30 | 4G | 8 GB |
| Paper, große Plugin-Suite, Datenbank | 30 bis 80 | 6G bis 8G | 12 bis 16 GB |
| Leichtes Modpack, bis 120 Mods | bis 10 | 6G | 8 GB |
| Mittleres Modpack, 150 bis 250 Mods | bis 20 | 8G bis 10G | 16 GB |
| Schweres Modpack, ab 300 Mods | bis 20 | 10G bis 12G | 16 bis 24 GB |
| Proxy (Velocity, BungeeCord) | beliebig | 512M bis 1G | 2 GB |
Zwei Hinweise dazu. Erstens skaliert der Heapbedarf bei Modpacks fast ausschließlich mit der Anzahl der Mods und der Weltgröße, kaum mit der Spielerzahl. Zweitens ist view-distance in der server.properties der wirksamste Hebel überhaupt: Von 10 auf 8 herunterzugehen spart oft mehr Speicher als 2 GB zusätzlicher Heap, weil die Zahl der geladenen Chunks quadratisch mit der Sichtweite wächst. simulation-distance auf 6 zu setzen wirkt zusätzlich auf die CPU-Last.
Xms gleich Xmx: das Aufwärmen
-Xms legt fest, mit wie viel Heap die JVM startet. Steht dort ein kleinerer Wert als bei -Xmx, wächst der Heap im Betrieb Stück für Stück. Jede Vergrößerung bedeutet eine Vollsammlung der Garbage Collection und frische Page Faults beim Betriebssystem, und weil G1 den Heap auch wieder schrumpfen lässt, wiederholt sich das Spiel. Genau daher kommen die berüchtigten Ruckler alle paar Minuten in den ersten Stunden nach dem Start.
Setzen Sie -Xms immer gleich -Xmx. Ergänzt um -XX:+AlwaysPreTouch fasst die JVM beim Start jede einzelne Speicherseite des Heaps einmal an. Der Start dauert dadurch je nach Heapgröße 2 bis 15 Sekunden länger, dafür passieren im Spielbetrieb keine Page Faults mehr. Der angenehme Nebeneffekt: Wenn -Xmx zu groß gewählt ist, fällt das in aller Regel schon beim Start auf und nicht erst drei Stunden später mitten im Spielbetrieb.
Verlassen Sie sich dabei aber nicht auf einen kurzen Probelauf mit -version. Nachgemessen: java -Xms4G -Xmx4G -XX:+AlwaysPreTouch -version lief in einer auf 2 GB begrenzten Umgebung anstandslos durch, weil der Aufruf endet, bevor der Heap tatsächlich in Benutzung ist. Ein sauberer Beweis ist erst der echte Serverstart mit anschließender Beobachtung von ps -o rss= -C java.
Ein bewährter Startbefehl für Paper sieht damit so aus (die sogenannten Aikar-Flags):
java -Xms6G -Xmx6G \
-XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 \
-XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch \
-XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M \
-XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 \
-XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 \
-XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 \
-XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/minecraft/dumps \
-XX:+ExitOnOutOfMemoryError \
-jar paper.jar nogui
Ab 12 GB Heap empfiehlt PaperMC angepasste Werte: G1NewSizePercent=40, G1MaxNewSizePercent=50, G1HeapRegionSize=16M, G1ReservePercent=15 und InitiatingHeapOccupancyPercent=20.
Die beiden letzten Zeilen sind der eigentliche Gewinn und fehlen in fast jeder Anleitung. -XX:+HeapDumpOnOutOfMemoryError schreibt beim Absturz ein vollständiges Speicherabbild, mit dem sich die Ursache später nachweisen lässt. -XX:+ExitOnOutOfMemoryError beendet die JVM sofort, statt sie in einem halbtoten Zustand weiterlaufen zu lassen, in dem Spieler sich verbinden und Fortschritt verlieren. Zusammen mit Restart=on-failure in der Unit-Datei entsteht daraus ein sauberer Neustart, siehe dazu Minecraft-Server automatisch starten und systemd-Service erstellen.
Rechnen Sie ein: Das Dump-Verzeichnis muss existieren und mindestens so viel Platz haben wie -Xmx. Ein 8-GB-Heap erzeugt eine 8 GB große .hprof-Datei. Wenn die Platte danach voll ist, haben Sie ein zweites Problem, siehe Festplatte voll unter Linux.
Java-Version und Systemunterschiede
Welche JVM Sie einsetzen, ändert das Verhalten spürbar:
- Java 8 nutzt standardmäßig den Parallel-Collector, nicht G1. Hier taucht auch die Variante
java.lang.OutOfMemoryError: GC overhead limit exceededauf, die bedeutet, dass über 98 Prozent der Zeit in der Garbage Collection verbracht wurde. Die GC-Protokollierung läuft über-XX:+PrintGCDetails -Xloggc:gc.log. - Ab Java 9 ist G1 auf allen Maschinen mit mindestens zwei Kernen und 1792 MB RAM der Standard. Die Protokollierung läuft über das neue Unified Logging:
-Xlog:gc*:file=logs/gc.log:time,uptime:filecount=5,filesize=10M. Die alten Flags werden hier abgelehnt, der Server startet nicht. - Paketlage: Debian 13 liefert
openjdk-21-jre-headlessundopenjdk-25-jre-headless, aber kein Java 17. Debian 12 liefert Java 17, aber weder 21 noch 25. Ubuntu 22.04 und 24.04 haben 8, 11, 17, 21 und 25. Wer eine bestimmte Version braucht, die die Distribution nicht kennt, nimmt das Adoptium-Repository (Temurin 8 bis 26 für trixie, bookworm, noble und jammy). Details in Java 21 auf Debian installieren und Java 17 auf Debian installieren.
Die Stolperfalle, die fast jeden trifft: Die Diagnosewerkzeuge jcmd, jmap, jstat und jstack sind in den jre-headless-Paketen nicht enthalten. Sie stecken in openjdk-XX-jdk-headless. Wer den Server mit dem JRE betreibt, steht im Ernstfall ohne Werkzeug da:
apt install -y openjdk-21-jdk-headless
dnf install -y java-21-openjdk-devel
Die erste Zeile gilt für Debian und Ubuntu, die zweite für AlmaLinux, Rocky Linux und Oracle Linux. Danach liegen jcmd und jmap unter /usr/bin/. Prüfen Sie das mit command -v jcmd und nicht mit which jcmd: In der Minimalinstallation der Red-Hat-Familie fehlt which, und unter EL 10 ist es ohnehin abgekündigt.
Speicherlecks durch Plugins erkennen
Ein Speicherleck sieht anders aus als schlichter Speichermangel. Der Unterschied liegt im Verlauf: Bei zu kleinem Heap pendelt sich der Verbrauch nach dem Start auf einem hohen Niveau ein und bleibt dort. Bei einem Leck steigt der Grundwert nach jeder vollständigen Garbage Collection weiter an. Genau dieser Grundwert ist die Kennzahl, auf die es ankommt.
Er lässt sich direkt abfragen. Zuerst die Prozess-ID ermitteln, dann eine Vollsammlung erzwingen und den Heap ansehen:
pgrep -f paper.jar
jmap -histo:live PID | head -30
jcmd PID GC.heap_info
Wichtiger Hinweis: jmap -histo:live löst intern eine vollständige Sammlung aus und funktioniert auch dann, wenn wie oben -XX:+DisableExplicitGC gesetzt ist. Der naheliegende Befehl jcmd PID GC.run tut in dieser Konstellation nichts, weil er über System.gc() läuft und genau das abgeschaltet wurde. Das kostet erfahrungsgemäß eine halbe Stunde Verwirrung.
Notieren Sie den Wert direkt nach dem Start, dann nach zwei, sechs und zwölf Stunden. Steigt der Wert nach der Vollsammlung stetig, ohne dass mehr Spieler online sind, ist es ein Leck. Die Klassenliste aus dem Histogramm zeigt meist schon, wohin es geht: massenhaft ItemStack, CraftPlayer von längst ausgeloggten Spielern oder ein Objekt mit dem Paketnamen eines Plugins.
Deutlich bequemer geht das mit dem Plugin spark, das für Paper, Fabric und Forge verfügbar ist:
/spark healthreport --memoryzeigt Heap-Auslastung, GC-Verhalten und Nicht-Heap-Bereiche auf einen Blick./spark heapsummaryerzeugt eine Rangliste der Klassen nach Speicherverbrauch, ohne dabei einen mehrere Gigabyte großen Dump zu schreiben./spark gczeigt Häufigkeit und Dauer der Sammlungen.
Wenn der Verdacht auf ein bestimmtes Plugin fällt, ist der Gegenbeweis simpel: Plugin entfernen, Server 24 Stunden laufen lassen, Grundwert erneut messen. Klassische Kandidaten sind Plugins, die Spielerdaten in Maps zwischenspeichern und beim Ausloggen nicht aufräumen, sowie alles, was mit Weltbearbeitung zu tun hat und eine unbegrenzte Rückgängig-Historie führt.
Swap: Rettungsanker, keine Speichererweiterung
Swap und ein Java-Heap sind natürliche Feinde. Die Garbage Collection tastet regelmäßig große Teile des Heaps an. Liegt auch nur ein Bruchteil davon auf der Platte, wird aus einer 50-Millisekunden-Pause eine von 20 Sekunden, und der Server gilt als eingefroren. Rechnen Sie Swap niemals in die Heapgröße ein.
Trotzdem sollte Swap vorhanden sein, nur eben klein und träge. Er wirkt als Puffer, damit kurzzeitige Spitzen nicht sofort den OOM-Killer auslösen, und er nimmt selten benutzte Seiten anderer Dienste auf. Empfehlenswert sind 2 bis 4 GB und eine niedrige Auslagerungsneigung:
swapon --show
cat /proc/sys/vm/swappiness
sysctl -w vm.swappiness=10
Dauerhaft gesetzt wird das in /etc/sysctl.d/99-swappiness.conf. Die Einrichtung im Detail steht in Swap einrichten und Out-of-Memory vermeiden.
Ein Sonderfall verdient Beachtung: -XX:+AlwaysPreTouch in Kombination mit zu wenig RAM. Da beim Start jede Heap-Seite angefasst wird, wandert der überschüssige Teil sofort in den Swap. Der Server startet dann zwar, ist aber vom ersten Tick an unbrauchbar langsam. Wenn ein Server nach dem Setzen von PreTouch plötzlich extrem träge startet, ist -Xmx zu groß, nicht PreTouch schuld.
Als harte Obergrenze lässt sich zusätzlich MemoryMax in der systemd-Unit setzen, etwa MemoryMax=7G bei 8 GB RAM. Dann trifft der Kernel bei einer Entgleisung gezielt den Minecraft-Prozess und nicht die Datenbank oder den SSH-Zugang.
Woran Sie erkennen, dass es wirklich behoben ist
Ein Neustart ohne sofortigen Absturz ist kein Beweis. Prüfen Sie stattdessen diese fünf Punkte nach 24 Stunden Betrieb unter normaler Last:
jcmd PID GC.heap_info: Der belegte Heap direkt nach einer Vollsammlung sollte deutlich unter 70 Prozent von-Xmxliegen und über die Zeit stabil bleiben.ps -o rss= -C java: Der Wert sollte sich bei ungefährXmxplus 1 bis 1,5 GB einpendeln und nicht weiter klettern. Steigt er, während der Heap stabil bleibt, liegt das Leck außerhalb des Heaps, typischerweise im Metaspace oder in Direct Buffers.free -h: available sollte nie unter etwa 500 MB fallen.swapon --show: Die belegte Swap-Menge sollte nahe null bleiben.- Das GC-Log: Vollständige Sammlungen ("Pause Full") sollten praktisch nicht vorkommen, die üblichen Pausen unter 200 Millisekunden bleiben. Reihenweise Full GCs kurz hintereinander sind das sichere Vorzeichen des nächsten
OutOfMemoryError, oft eine Viertelstunde vorher.
Zusätzlich zeigt /tps beziehungsweise /spark tps im Spiel, ob die Tickrate stabil bei 20,0 liegt. Ein Server, der speichertechnisch gesund ist, hält diesen Wert auch nach Stunden.
Wenn Java gar nicht erst startet
Vier Meldungen im Wortlaut, die beim Anpassen der Speicherwerte typischerweise auftreten:
Invalid maximum heap size: -Xmx8GBist der häufigste Tippfehler. Die Einheit heißtG, nichtGB. Erlaubt sindk,mundg, groß oder klein geschrieben.Initial heap size set to a larger value than the maximum heap sizebedeutet, dass-Xmsgrößer ist als-Xmx, meist weil beim Kopieren nur einer der beiden Werte angepasst wurde.Could not reserve enough space for object heapheißt, dass die Zuweisung den verfügbaren Speicher übersteigt. Auf einer 32-Bit-JVM ist bei knapp 4 GB in jedem Fall Schluss, unabhängig vom vorhandenen RAM. Prüfen mitjava -version, dort muss64-Bit Server VMstehen.Unrecognized VM option 'UseG1GC'oder ähnlich weist auf eine zu alte oder falsche JVM hin. Manche experimentellen Flags brauchen zwingend ein vorangestelltes-XX:+UnlockExperimentalVMOptions, und zwar vor dem betreffenden Flag in der Befehlszeile.
Womit die JVM am Ende tatsächlich arbeitet, lässt sich jederzeit gegenprüfen. Ohne laufenden Server über die Standardwerte:
java -XX:+PrintFlagsFinal -version 2>/dev/null | grep -w MaxHeapSize
Zwei Kleinigkeiten daran sind Absicht. Das 2>/dev/null schluckt das Versionsbanner, das die JVM auf die Fehlerausgabe schreibt und das sonst ungefiltert am grep vorbei mit in der Ausgabe landet. Und grep -w MaxHeapSize statt grep -i maxheapsize liefert wirklich nur eine Zeile: Die unscharfe Suche findet zusätzlich SoftMaxHeapSize, und wer nur einen Wert erwartet, liest dann leicht den falschen ab.
Und am laufenden Prozess über die tatsächlich aktiven Flags:
jcmd PID VM.flags
Das ist der zuverlässigste Weg, um die erstaunlich verbreitete Situation aufzudecken, dass ein Startskript zwar geändert wurde, der Server aber noch mit den alten Werten aus einer zweiten Skriptdatei läuft.
Zusammengefasst: Messen Sie zuerst, ob überhaupt der Heap das Problem ist. Weisen Sie dann so viel zu, wie die Welt braucht, und lassen Sie mindestens 1,5 GB für JVM und System übrig. Setzen Sie -Xms gleich -Xmx, aktivieren Sie den Heap-Dump für den Ernstfall und beobachten Sie den Grundwert nach der Vollsammlung über mehrere Stunden. Der Unterschied zwischen "läuft" und "läuft stabil" liegt genau in diesem letzten Schritt.
Für die Grundeinrichtung des Servers selbst finden Sie die passenden Schritte unter Minecraft-Server auf Debian installieren und Checkliste für einen neuen Rootserver.
Häufige Fragen
Wie viel RAM soll ich meinem Minecraft-Server zuweisen?
Warum soll Xms gleich Xmx sein?
Was ist der Unterschied zwischen OutOfMemoryError und einem Prozess, der einfach mit Killed verschwindet?
Wie erkenne ich ein Speicherleck durch ein Plugin?
Hilft mehr Swap gegen den Java-heap-space-Fehler?
Warum finde ich jcmd und jmap auf meinem Server nicht?
2026 KernelHost GmbH. Alle Rechte vorbehalten. Diese Anleitung ist urheberrechtlich geschützt. Eine Veröffentlichung auf anderen Webseiten, auch auszugsweise oder in bearbeiteter Form, ist ohne unsere schriftliche Zustimmung nicht gestattet. Zitate mit Quellenangabe und Link sind ausdrücklich willkommen.

