Project Zomboid Field guide

Project Zomboid server memory optimization: JVM, garbage collection and lag

Memory optimization for Project Zomboid multiplayer servers: RAM sizing for mods and player counts, -Xms/-Xmx heap limits, and G1GC garbage collection tuning.

Project Zomboid server memory optimization: JVM, garbage collection and lag

The Project Zomboid dedicated server runs on the OpenJDK 17 Java Virtual Machine. During extended multiplayer campaigns, particularly when players travel at high speeds across unfamiliar map tiles, spawn hordes of zombies, or install extensive Workshop mods, the server process frequently experiences stuttering and abrupt RAM spikes. Without proper JVM parameter tuning, the server suffers from extended Stop-The-World garbage collection freezes or gets terminated by the Linux out-of-memory killer.

This practical operational guide explains RAM requirements calculation, configuring -Xms and -Xmx heap limits, tuning the G1GC garbage collector, and monitoring Java heap pressure in real time.

Calculating RAM requirements for multiplayer servers#

Stable Project Zomboid hosting requires dividing available physical memory between the Java Heap and host system overhead.

Core planning guidelines:

  • Baseline empty world: 4 GB RAM (Knox County world metadata, room definitions, and zombie spawn grids).
  • Player headroom: +500 MB RAM per concurrent player (inventory tracking, chunk view radius, and packet serialization).
  • Workshop modpacks: +2–4 GB RAM depending on custom vehicle counts, firearms, and tile expansions.
  • Operating system reserve: At least 2 GB unallocated RAM for Linux page caching, network buffers, and native C++ shared libraries.

On a physical host with 16 GB of RAM, set the maximum heap -Xmx to 12G or 13G. Assigning all 16 GB directly to Java will cause the Linux kernel to terminate the server under memory pressure.

Configuring -Xms and -Xmx in ProjectZomboid64.json#

JVM arguments are defined inside ProjectZomboid64.json or within start-server.sh. Edit the launch configuration file:

bash
nano /opt/pzserver/ProjectZomboid64.json

Set identical initial (-Xms) and maximum (-Xmx) heap boundaries to eliminate expensive runtime heap resizing during active gameplay:

json
{
  "mainClass": "zombie/network/GameServer",
  "classpath": [
    "."
  ],
  "vmArgs": [
    "-Xms12G",
    "-Xmx12G",
    "-XX:+UseG1GC",
    "-XX:MaxGCPauseMillis=20",
    "-XX:InitiatingHeapOccupancyPercent=45",
    "-XX:G1ReservePercent=15",
    "-Djava.awt.headless=true"
  ]
}

If the server operates as a systemd service, verify /etc/systemd/system/zomboid.service and configure the unit memory ceiling to accommodate native overhead:

ini
[Service]
MemoryMax=14G
LimitNOFILE=65535

Apply the updated service definition:

bash
sudo systemctl daemon-reload
sudo systemctl restart zomboid

When operating through a modern server management platform, heap allocation and JVM flags are managed through dynamic resource sliders with automated OOM thresholds.

Tuning the G1GC garbage collector to prevent tick stutter#

Default Java garbage collectors can induce Stop-The-World freezes lasting anywhere from 500 ms to 3 seconds when Old Generation memory fills up. In real-time survival gameplay, this leads to desynchronized vehicles and phantom zombie strikes.

Key G1GC optimization flags for Project Zomboid:

  • -XX:+UseG1GC: Enables the Garbage-First collector, partitioning the heap into autonomous memory regions.
  • -XX:MaxGCPauseMillis=20: Sets a target pause threshold of 20 milliseconds, staying well within a standard tick window.
  • -XX:InitiatingHeapOccupancyPercent=45: Triggers concurrent marking cycles early once the old generation reaches 45% capacity.
  • -XX:G1ReservePercent=15: Reserves 15% spare heap headroom to prevent evacuation failure stalls.

Monitoring heap utilization with jstat and htop#

To measure real-time heap dynamics, find the PID of the active game server process:

bash
pgrep -f "zombie.network.GameServer"

Launch the Java statistics utility jstat to sample garbage collection metrics every second:

bash
jstat -gcutil $(pgrep -f "zombie.network.GameServer") 1000

Key output columns to evaluate:

  • E (Eden): Percentage of young generation capacity currently filled.
  • O (Old): Percentage of old generation capacity currently utilized.
  • FGC (Full GC Count): Total full-heap collection pauses. On a well-tuned server, this count must remain 0.

Verify broader host memory allocation using free -h:

bash
free -h

Resolving common memory misconfigurations#

  1. Process crashes abruptly with exit code 137 (Killed): The Linux OOM-killer terminated the Java process due to host memory starvation. Inspect the kernel ring buffer:
bash
sudo dmesg -T | grep -i oom

If ProjectZomboid6 was selected by the OOM killer, decrease -Xmx by 1–2 GB to provide adequate headroom for native allocations.

  1. Uncontrolled memory growth caused by custom mod textures: Certain vehicle and map mods fail to purge cached texture objects. Schedule a clean daily restart via cron:
bash
sudo crontab -l | grep zomboid

Add an automated restart task scheduled at 05:00 UTC:

bash
0 5 * * * /usr/bin/systemctl restart zomboid

Sources#