Minecraft-server: "java.lang.OutOfMemoryError: Java heap space" oplossen
De melding java.lang.OutOfMemoryError: Java heap space betekent niet automatisch te weinig RAM. Zo dimensioneert u Xmx en Xms juist, spoort u geheugenlekken op en zet u swap verstandig in.
De server draait al drie uur, dan loopt hij vast, de tickrate zakt naar 2 en in de console staat:
[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(...)
De eerste reflex is bijna altijd dezelfde: meer RAM toewijzen. In ongeveer de helft van de gevallen is dat precies de verkeerde reactie, en in een deel daarvan wordt de zaak er meetbaar erger van. Dit artikel laat zien wat de melding werkelijk betekent, hoe u -Xmx en -Xms netjes dimensioneert, hoe u een geheugenlek van een echt geheugentekort onderscheidt en waaraan u ziet dat de oplossing standhoudt.
Wat de melding precies betekent, en wat niet
De Java-heap is het gebied waarin de JVM objecten opslaat: geladen chunks, entities, inventarissen en gegevens van plug-ins. De bovengrens daarvan legt u vast met -Xmx. Een OutOfMemoryError: Java heap space betekent: de JVM wilde een object aanmaken, de heap zat vol en de garbage collection kon niet genoeg ruimte vrijmaken. Over het vrije geheugen van het besturingssysteem zegt dat niets. Een server met 64 GB RAM geeft de melding net zo betrouwbaar wanneer -Xmx2G is ingesteld en de wereld 4 GB nodig heeft.
Daarvan moet u twee andere storingen goed onderscheiden, die er vaak mee verward worden:
- Het proces verdwijnt zonder stacktrace, in het log staat alleen
Killedof in het journalMain process exited, code=killed, status=9/KILL. Dat was de kernel, niet de JVM. De Linux-OOM-killer heeft toegeslagen omdat het volledige geheugen van het systeem op was. Controleren kunt u dat metdmesg | grep -i "out of memory", daar staat dan een regel alsOut of memory: Killed process 1337 (java). - Java start helemaal niet en meldt
Error occurred during initialization of VM / Could not reserve enough space for object heap. Dan is-Xmxgroter dan wat het systeem kan leveren.
Dit onderscheid is de belangrijkste stap. Bij variant één is er te weinig heap, bij variant twee en drie te veel. Wie de gevallen verwisselt, draait aan de verkeerde knop.
Eerst meten, dan toewijzen
Voordat u ook maar één waarde wijzigt, hebt u twee getallen nodig: het werkelijk beschikbare geheugen en het huidige verbruik.
free -h
cat /proc/meminfo | grep -E 'MemTotal|MemAvailable|SwapTotal'
Doorslaggevend is MemAvailable, niet free. Linux gebruikt ongebruikt RAM als bestandscache, "free" is daarom bijna altijd klein en bijna altijd irrelevant.
Het werkelijke verbruik van de server ziet u zo:
ps -o pid,rss,cmd -C java
De RSS-waarde staat in kilobytes en is het geheugen dat het proces in RAM in beslag neemt. Die waarde is altijd groter dan -Xmx, en precies daar houden de meeste handleidingen op.
Waarom "zo veel mogelijk" gegarandeerd misgaat
Naast de heap heeft de JVM nog een hele reeks andere geheugengebieden nodig, die helemaal niet onder -Xmx vallen:
- Metaspace: de geladen klassen. Bij een modpack met 300 mods is dat al snel 300 tot 600 MB.
- Thread-stacks: elke thread krijgt ongeveer 1 MB. Chunk-workers, Netty-threads en de schedulers van plug-ins, dat telt op tot 100 tot 300 MB.
- Direct buffers: Netty handelt het volledige netwerkverkeer af via geheugen buiten de heap. Bij veel gelijktijdige spelers zijn enkele honderden megabytes normaal.
- Code-cache en GC-structuren: de JIT-compiler en de boekhouding van G1 zelf kosten grofweg 5 tot 10 procent van de heap.
Als betrouwbare vuistregel geldt: reken op Xmx plus 1 tot 1,5 GB voor de JVM, plus minstens 512 MB voor het besturingssysteem. Bij een modpack met veel mods eerder Xmx plus 2 GB.
Op een server met 8 GB RAM betekent dat -Xmx6G en niet -Xmx8G. Wie 8 toewijst, krijgt geen heapfout meer, maar iets ergers: een proces dat zonder waarschuwing door de kernel wordt afgeschoten, midden in het opslaan van de wereld. De heapfout is een nette, gedocumenteerde storing. De OOM-kill kan beschadigde regiobestanden achterlaten.
Er is nog een tweede reden tegen maximale toewijzing: een te grote heap maakt de garbage collection trager. G1 moet meer geheugen doorzoeken, de pauzes bij een mixed GC worden langer, en van een incidentele hapering wordt een merkbare freeze. Boven ongeveer 12 GB slaat die verhouding bij Minecraft doorgaans om naar het negatieve. Wie meer nodig heeft, kan de wereld beter opsplitsen dan de heap vergroten.
Vuistregels naar aantal spelers en modpack
Deze waarden zijn startpunten, geen natuurwetten. Ze gaan uit van een normaal grote wereld en actieve pregeneratie.
| Servertype | Spelers | Xmx | RAM in het systeem |
|---|---|---|---|
| Vanilla of Paper, zonder plug-ins | tot 10 | 2G | 4 GB |
| Paper met 15 tot 30 plug-ins | 10 tot 30 | 4G | 8 GB |
| Paper, grote verzameling plug-ins, database | 30 tot 80 | 6G tot 8G | 12 tot 16 GB |
| Licht modpack, tot 120 mods | tot 10 | 6G | 8 GB |
| Middelzwaar modpack, 150 tot 250 mods | tot 20 | 8G tot 10G | 16 GB |
| Zwaar modpack, vanaf 300 mods | tot 20 | 10G tot 12G | 16 tot 24 GB |
| Proxy (Velocity, BungeeCord) | willekeurig | 512M tot 1G | 2 GB |
Twee opmerkingen daarbij. Ten eerste schaalt de heapbehoefte bij modpacks vrijwel uitsluitend mee met het aantal mods en de grootte van de wereld, nauwelijks met het aantal spelers. Ten tweede is view-distance in de server.properties verreweg de effectiefste knop: van 10 naar 8 gaan bespaart vaak meer geheugen dan 2 GB extra heap, omdat het aantal geladen chunks kwadratisch met de kijkafstand groeit. simulation-distance op 6 zetten werkt daarnaast op de CPU-belasting.
Xms gelijk aan Xmx: het opwarmen
-Xms bepaalt met hoeveel heap de JVM start. Staat daar een kleinere waarde dan bij -Xmx, dan groeit de heap tijdens het draaien stukje bij beetje. Elke vergroting betekent een volledige garbage collection en verse page faults bij het besturingssysteem, en omdat G1 de heap ook weer laat krimpen, herhaalt dat spel zich telkens opnieuw. Precies daar komen de beruchte haperingen om de paar minuten vandaan, in de eerste uren na de start.
Stel -Xms altijd gelijk in aan -Xmx. Aangevuld met -XX:+AlwaysPreTouch raakt de JVM bij de start elke afzonderlijke geheugenpagina van de heap één keer aan. De start duurt daardoor, afhankelijk van de heapgrootte, 2 tot 15 seconden langer, maar tijdens het spelen treden er geen page faults meer op. Het prettige neveneffect: is -Xmx te groot gekozen, dan valt dat in de regel al bij de start op en niet pas drie uur later, midden in het spel.
Vertrouw daarbij echter niet op een korte proefrun met -version. Nagemeten: java -Xms4G -Xmx4G -XX:+AlwaysPreTouch -version liep in een tot 2 GB begrensde omgeving probleemloos door, omdat de aanroep eindigt voordat de heap daadwerkelijk in gebruik is. Een sluitend bewijs levert pas de echte serverstart, met daarna observatie van ps -o rss= -C java.
Een beproefd startcommando voor Paper ziet er daarmee zo uit (de zogenoemde 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
Vanaf 12 GB heap adviseert PaperMC aangepaste waarden: G1NewSizePercent=40, G1MaxNewSizePercent=50, G1HeapRegionSize=16M, G1ReservePercent=15 en InitiatingHeapOccupancyPercent=20.
De laatste twee regels leveren de eigenlijke winst op en ontbreken in bijna elke handleiding. -XX:+HeapDumpOnOutOfMemoryError schrijft bij een crash een volledige geheugendump weg, waarmee u de oorzaak later kunt aantonen. -XX:+ExitOnOutOfMemoryError beëindigt de JVM meteen, in plaats van hem in een halfdode toestand door te laten draaien waarin spelers verbinding maken en voortgang verliezen. Samen met Restart=on-failure in het unit-bestand ontstaat daaruit een nette herstart, zie daarvoor Minecraft-server automatisch starten en systemd-service aanmaken.
Houd hier rekening mee: de dumpmap moet bestaan en minstens zo veel ruimte hebben als -Xmx. Een heap van 8 GB levert een .hprof-bestand van 8 GB op. Is de schijf daarna vol, dan hebt u een tweede probleem, zie Schijf vol onder Linux.
Java-versie en systeemverschillen
Welke JVM u inzet, verandert het gedrag merkbaar:
- Java 8 gebruikt standaard de parallel collector, niet G1. Hier duikt ook de variant
java.lang.OutOfMemoryError: GC overhead limit exceededop, die betekent dat meer dan 98 procent van de tijd in de garbage collection is doorgebracht. De GC-logging loopt via-XX:+PrintGCDetails -Xloggc:gc.log. - Vanaf Java 9 is G1 de standaard op alle machines met minstens twee cores en 1792 MB RAM. De logging loopt via de nieuwe unified logging:
-Xlog:gc*:file=logs/gc.log:time,uptime:filecount=5,filesize=10M. De oude flags worden hier geweigerd, de server start dan niet. - Pakketsituatie: Debian 13 levert
openjdk-21-jre-headlessenopenjdk-25-jre-headless, maar geen Java 17. Debian 12 levert Java 17, maar noch 21 noch 25. Ubuntu 22.04 en 24.04 hebben 8, 11, 17, 21 en 25. Wie een bepaalde versie nodig heeft die de distributie niet kent, neemt de Adoptium-repository (Temurin 8 tot 26 voor trixie, bookworm, noble en jammy). Details in Java 21 installeren op Debian en Java 17 installeren op Debian.
De valkuil waar bijna iedereen in trapt: de diagnosetools jcmd, jmap, jstat en jstack zitten niet in de jre-headless-pakketten. Ze zitten in openjdk-XX-jdk-headless. Wie de server met de JRE draait, staat als het erop aankomt zonder gereedschap:
apt install -y openjdk-21-jdk-headless
dnf install -y java-21-openjdk-devel
De eerste regel geldt voor Debian en Ubuntu, de tweede voor AlmaLinux, Rocky Linux en Oracle Linux. Daarna staan jcmd en jmap onder /usr/bin/. Controleer dat met command -v jcmd en niet met which jcmd: in de minimale installatie van de Red Hat-familie ontbreekt which, en onder EL 10 is het sowieso afgeschaft.
Geheugenlekken door plug-ins herkennen
Een geheugenlek ziet er anders uit dan simpelweg te weinig geheugen. Het verschil zit in het verloop: bij een te kleine heap stabiliseert het verbruik zich na de start op een hoog niveau en blijft daar. Bij een lek stijgt de basiswaarde na elke volledige garbage collection verder door. Precies die basiswaarde is het kengetal waar het om draait.
Die kunt u rechtstreeks opvragen. Bepaal eerst de proces-id, forceer daarna een volledige collectie en bekijk de heap:
pgrep -f paper.jar
jmap -histo:live PID | head -30
jcmd PID GC.heap_info
Belangrijke aanwijzing: jmap -histo:live zet intern een volledige collectie in gang en werkt ook wanneer, zoals hierboven, -XX:+DisableExplicitGC is ingesteld. Het voor de hand liggende commando jcmd PID GC.run doet in deze opzet niets, omdat het via System.gc() loopt en juist dat is uitgeschakeld. Dat kost ervaringsgewijs een half uur verwarring.
Noteer de waarde direct na de start, daarna na twee, zes en twaalf uur. Stijgt de waarde na de volledige collectie gestaag, zonder dat er meer spelers online zijn, dan is het een lek. De klassenlijst uit het histogram wijst meestal al de richting: massaal ItemStack, CraftPlayer van allang uitgelogde spelers of een object met de pakketnaam van een plug-in.
Veel comfortabeler gaat dat met de plug-in spark, die beschikbaar is voor Paper, Fabric en Forge:
/spark healthreport --memorytoont in één oogopslag het heapgebruik, het GC-gedrag en de gebieden buiten de heap./spark heapsummarymaakt een ranglijst van de klassen op geheugenverbruik, zonder daarbij een dump van meerdere gigabytes weg te schrijven./spark gctoont de frequentie en de duur van de collecties.
Valt de verdenking op een bepaalde plug-in, dan is het tegenbewijs simpel: plug-in verwijderen, de server 24 uur laten draaien, de basiswaarde opnieuw meten. Klassieke kandidaten zijn plug-ins die spelersgegevens in maps cachen en bij het uitloggen niet opruimen, plus alles wat met wereldbewerking te maken heeft en een onbeperkte undo-geschiedenis bijhoudt.
Swap: reddingsboei, geen geheugenuitbreiding
Swap en een Java-heap zijn natuurlijke vijanden. De garbage collection raakt regelmatig grote delen van de heap aan. Ligt daarvan ook maar een fractie op de schijf, dan wordt een pauze van 50 milliseconden er een van 20 seconden en geldt de server als vastgelopen. Reken swap nooit mee in de heapgrootte.
Toch hoort swap aanwezig te zijn, alleen dan klein en traag. Hij werkt als buffer, zodat kortstondige pieken niet meteen de OOM-killer in werking zetten, en hij vangt zelden gebruikte pagina's van andere services op. Aan te raden zijn 2 tot 4 GB en een lage neiging om te swappen:
swapon --show
cat /proc/sys/vm/swappiness
sysctl -w vm.swappiness=10
Permanent legt u dat vast in /etc/sysctl.d/99-swappiness.conf. Het inrichten in detail staat in Swap inrichten en out-of-memory voorkomen.
Een bijzonder geval verdient aandacht: -XX:+AlwaysPreTouch in combinatie met te weinig RAM. Omdat bij de start elke heappagina wordt aangeraakt, verhuist het overtollige deel meteen naar de swap. De server start dan wel, maar is vanaf de eerste tick onbruikbaar traag. Start een server na het instellen van PreTouch ineens extreem stroef, dan is -Xmx te groot en niet PreTouch de schuldige.
Als harde bovengrens kunt u daarnaast MemoryMax in de systemd-unit zetten, bijvoorbeeld MemoryMax=7G bij 8 GB RAM. Dan treft de kernel bij een ontsporing gericht het Minecraft-proces en niet de database of de SSH-toegang.
Waaraan u ziet dat het echt verholpen is
Een herstart zonder onmiddellijke crash is geen bewijs. Controleer in plaats daarvan deze vijf punten na 24 uur draaien onder normale belasting:
jcmd PID GC.heap_info: de bezette heap direct na een volledige collectie hoort duidelijk onder 70 procent van-Xmxte liggen en na verloop van tijd stabiel te blijven.ps -o rss= -C java: de waarde hoort zich rondXmxplus 1 tot 1,5 GB te stabiliseren en niet verder te klimmen. Stijgt die waarde terwijl de heap stabiel blijft, dan zit het lek buiten de heap, meestal in de metaspace of in direct buffers.free -h: available hoort nooit onder ongeveer 500 MB te zakken.swapon --show: de bezette hoeveelheid swap hoort dicht bij nul te blijven.- Het GC-log: volledige collecties ("Pause Full") horen praktisch niet voor te komen, de gebruikelijke pauzes horen onder 200 milliseconden te blijven. Rijen full GC's kort na elkaar zijn het zekere voorteken van de volgende
OutOfMemoryError, vaak een kwartier van tevoren.
Daarnaast laat /tps of /spark tps in het spel zien of de tickrate stabiel op 20,0 ligt. Een server die qua geheugen gezond is, houdt die waarde ook na uren vast.
Wanneer Java helemaal niet start
Vier meldingen in hun letterlijke bewoording, die bij het aanpassen van de geheugenwaarden vaak opduiken:
Invalid maximum heap size: -Xmx8GBis de meest voorkomende typefout. De eenheid heetG, nietGB. Toegestaan zijnk,meng, in hoofdletters of in kleine letters.Initial heap size set to a larger value than the maximum heap sizebetekent dat-Xmsgroter is dan-Xmx, meestal omdat bij het kopiëren maar één van beide waarden is aangepast.Could not reserve enough space for object heapbetekent dat de toewijzing het beschikbare geheugen overschrijdt. Op een 32-bits JVM houdt het bij krap 4 GB hoe dan ook op, ongeacht het aanwezige RAM. Controleren metjava -version, daar moet64-Bit Server VMstaan.Unrecognized VM option 'UseG1GC'of iets vergelijkbaars wijst op een te oude of verkeerde JVM. Sommige experimentele flags vereisen beslist een voorafgaand-XX:+UnlockExperimentalVMOptions, en wel vóór de betreffende flag op de commandoregel.
Waarmee de JVM uiteindelijk daadwerkelijk werkt, kunt u op elk moment natrekken. Zonder draaiende server via de standaardwaarden:
java -XX:+PrintFlagsFinal -version 2>/dev/null | grep -w MaxHeapSize
Twee kleinigheden daaraan zijn met opzet zo. De 2>/dev/null slikt de versiebanner in die de JVM naar de foutuitvoer schrijft en die anders ongefilterd langs de grep heen in de uitvoer belandt. En grep -w MaxHeapSize in plaats van grep -i maxheapsize levert echt maar één regel op: de onscherpe zoekopdracht vindt ook SoftMaxHeapSize, en wie maar één waarde verwacht, leest dan gemakkelijk de verkeerde af.
En bij het draaiende proces via de daadwerkelijk actieve flags:
jcmd PID VM.flags
Dat is de betrouwbaarste manier om de verrassend vaak voorkomende situatie boven tafel te krijgen dat een startscript wel is gewijzigd, maar de server nog met de oude waarden uit een tweede scriptbestand draait.
Samengevat: meet eerst of de heap eigenlijk wel het probleem is. Wijs daarna zo veel toe als de wereld nodig heeft en houd minstens 1,5 GB over voor de JVM en het systeem. Stel -Xms gelijk aan -Xmx, zet de heapdump aan voor noodgevallen en volg de basiswaarde na de volledige collectie over meerdere uren. Het verschil tussen "draait" en "draait stabiel" zit precies in die laatste stap.
Voor het basisinrichten van de server zelf vindt u de bijbehorende stappen onder Minecraft-server installeren op Debian en Checklist voor een nieuwe rootserver.
Veelgestelde vragen
Hoeveel RAM moet ik aan mijn Minecraft-server toewijzen?
Waarom moet Xms gelijk zijn aan Xmx?
Wat is het verschil tussen een OutOfMemoryError en een proces dat gewoon met Killed verdwijnt?
Hoe herken ik een geheugenlek door een plug-in?
Helpt meer swap tegen de Java-heap-space-fout?
Waarom vind ik jcmd en jmap niet op mijn server?
2026 KernelHost GmbH. Alle rechten voorbehouden. Deze handleiding is auteursrechtelijk beschermd. Publicatie op andere websites, geheel, gedeeltelijk of in bewerkte vorm, is zonder onze schriftelijke toestemming niet toegestaan. Citeren met bronvermelding en link is uitdrukkelijk welkom.

