Minecraft Field guide

Minecraft server out of memory: heap error or Linux OOM kill?

Separate Java heap exhaustion, native memory failures and Linux or container OOM kills before changing Minecraft RAM settings.

Minecraft server out of memory: heap error or Linux OOM kill?

Before increasing Minecraft's RAM allocation, identify what ended the process. A Java heap exception, a native-memory failure and a Linux OOM kill need different investigations. A larger -Xmx can make a host or container shortage worse.

This guide uses an existing Paper server on Ubuntu 24.04 with sudo SSH access. The examples refer to minecraft.service and /srv/minecraft from the setup guide. Adjust those names for your installation. Container users must also inspect the container's limits through its management system; the VPS's total RAM is not necessarily the server's allowance.

Preserve the failure before stopping a restart loop#

Record the incident time, player activity and the most recent change. Keep the service and kernel logs together; a short tail from latest.log may miss the original cause. On the VPS, create a private evidence directory:

bash
(
set -e
umask 077
MC_EVIDENCE="$HOME/minecraft-memory-$(date +%F-%H%M%S)"
mkdir "$MC_EVIDENCE"
sudo journalctl -u minecraft --since '30 minutes ago' --no-pager > "$MC_EVIDENCE/service.log"
sudo journalctl -k --since '30 minutes ago' --no-pager > "$MC_EVIDENCE/kernel.log"
sudo systemctl show minecraft -p Result -p NRestarts -p ExecMainCode -p ExecMainStatus -p ExecStart > "$MC_EVIDENCE/service-state.txt"
)

Retain the relevant game crash report as well, if one exists. Inspect available disk space before collecting large artifacts. If the service repeatedly starts and fails, stop it after recording the evidence:

bash
sudo systemctl stop minecraft
sudo journalctl -u minecraft -n 60 --no-pager

This ends the maintenance session for players, but avoids repeatedly reloading the world while you investigate. Check whether shutdown completed cleanly; a process that was OOM-killed could not perform its normal final save. Stopping the service does not repair that earlier interruption. Preserve existing backups and world files before further changes.

Classify the message you actually have#

The Oracle memory troubleshooting guide distinguishes exhausted JVM memory areas. Keep the detail after OutOfMemoryError, rather than grouping every exception as a small heap.

EvidenceWhat it supportsNext investigation
OutOfMemoryError: Java heap spaceA heap allocation failedHeap size, live data and triggering workload
OutOfMemoryError: MetaspaceClass-metadata memory exhaustedClass loading, plugins and any explicit Metaspace cap
Native allocation failure or unable to create native threadA non-heap allocation or thread creation failedNative memory, thread counts and process limits
Kernel OOM record names the Java PIDLinux killed that process under memory pressureHost versus memory-cgroup constraint in the same record
Only SIGKILL, Killed or exit 137Abrupt terminationKernel, service manager and provider/container evidence; cause still unknown

A kill signal can come from an administrator, a stop timeout or another supervisor. Missing kernel records may reflect inaccessible or rotated logs. Do not turn their absence into proof that RAM was sufficient, or an unexplained exit into proof of OOM. Watchdog failures and UnsupportedClassVersionError belong to different branches; use the Java version guide for the latter.

An exception names a JVM memory pool; a kernel OOM record points to the host or cgroup boundary. Exit 137 alone leaves the cause unresolved.

Compare heap, process and whole-host memory#

-Xmx caps the Java heap, not the entire Java process. JVM stacks, class metadata and other native allocations sit outside it. Linux, filesystem cache and other services also need memory. Check the actual ExecStart or wrapper for heap options; percentage-based flags and panel-generated arguments can change the allocation you thought you selected.

During a controlled running observation:

bash
free -h
vmstat 1 10
sudo systemctl show minecraft -p MainPID -p MemoryCurrent -p MemoryHigh -p MemoryMax
MC_PID="$(systemctl show minecraft -p MainPID --value)"
if [ "$MC_PID" -gt 0 ]; then
  sudo ps -p "$MC_PID" -o pid,rss,vsz,args
fi

In free's upstream manual, available estimates memory that can be used without swapping; low free alone is not a shortage. vmstat reports swap-in and swap-out as si and so; compare later rows with the incident because the first is an average since boot. Persistent activity is a reason to inspect pressure, not a recipe to disable swap.

The ps manual distinguishes RSS, resident process memory, from VSZ, virtual address space. Neither equals live Java heap. If the main PID is a shell wrapper, identify Java in systemctl status and inspect that process instead. Compare samples with similar player activity; a post-restart idle reading cannot explain the pre-crash peak.

Check the service's memory boundary#

A host can have available RAM while a constrained service or container reaches its own limit. systemd's resource controls distinguish the pressure threshold MemoryHigh from the hard boundary MemoryMax. Parent slices can impose a tighter constraint even when the unit reports infinity.

For a running native systemd service on a cgroup-v2 host:

bash
MC_CGROUP="$(systemctl show minecraft -p ControlGroup --value)"
if [ -n "$MC_CGROUP" ] && [ "$MC_CGROUP" != '/' ]; then
  for MC_FILE in memory.current memory.max memory.events; do
    MC_PATH="/sys/fs/cgroup$MC_CGROUP/$MC_FILE"
    if [ -r "$MC_PATH" ]; then
      printf '%s\n' "$MC_PATH"
      cat "$MC_PATH"
    fi
  done
fi

The kernel cgroup documentation defines these files: memory amounts use bytes, max denotes an unlimited local boundary, and event counters accumulate. Compare counters before and after a controlled incident. oom_kill records OOM victims belonging to that cgroup, including victims of a global OOM; it does not by itself identify which limit caused the kill. Correlate it with the kernel record and inherited limits. A restarted or removed cgroup may lose the previous counters.

If the files are missing, do not assume there is no limit. The service may be stopped, use another hierarchy or run inside a container namespace. Inspect parent cgroups and container/provider quotas through the administrator who owns them. Do not remove a quota that also protects the rest of the VPS.

Collect JVM evidence under a repeatable workload#

When the server can stay up safely, use an operator account and available spark permissions. In Minecraft chat:

text
/spark gc
/spark gcmonitor

The spark reference documents GC history and the monitoring toggle. Record the same player activity and times; run /spark gcmonitor again to switch it off after the observation. For sustained lag or long ticks, follow the separate spark profiling guide.

For a controlled Java 21/25 run, GC logging can retain evidence beyond a restart. Add -Xlog:gc*:file=logs/gc.log:time,uptime,level,tags:filecount=5,filesize=10M before -jar in the existing launch command, preserving its executable and other arguments. Confirm that /srv/minecraft/logs exists and is writable by the service user. Copy previous GC logs before restarting; rotation may replace old evidence. The Java command reference documents rotation and the heap flags.

A rising used-memory graph alone does not establish a leak. Look for a rising post-collection live set under comparable sustained workload. If it keeps growing, test the suspected plugin on a preserved copy or give its maintainer the evidence. A heap dump can pause the JVM and occupy substantial disk space; collect one only with a clear analysis plan and sufficient space, and restrict access to its contents.

Choose one change and repeat the observation#

For a heap exception with genuine host and quota headroom, test a measured heap increase or reduce the workload retaining data. For a host/cgroup OOM, reduce the total budget, move competing services or arrange more capacity within an explicit budget. Increasing the heap to the whole machine or container limit leaves no room for overhead. A 2 GiB heap on a 4 GiB VPS is an example allocation from our setup guide, not a capacity guarantee.

Metaspace or thread failures need their own evidence; changing -Xmx does not directly repair them. Linux thread creation limits can prevent a new thread even when the heap has space. Repeated scheduled restarts may temporarily hide accumulation while discarding the trend. Keep the original configuration, change one variable, and compare another equivalent session, including memory peaks and logs rather than uptime alone.

For supported instances on your own VPS, Nexardo's server monitoring can make routine resource observation easier. It does not identify an OOM cause on its own or automatically adopt this manual service.

After repair, reload any edited unit with sudo systemctl daemon-reload. If systemd hit its start-rate limit, run sudo systemctl reset-failed minecraft, then start it with sudo systemctl start minecraft during a maintenance window. Confirm game readiness and join with a client. A successful restart is the beginning of verification: reproduce the previous workload and check that the same memory failure no longer occurs.