Check Java in the running process
Execute java -version in the environment where the server is running. The service, the container, and your terminal may use different paths. The Paper table recommends Java 21 for 1.20–1.21.11, and Java 25 for 26.1+. Check your branch against Paper requirements, not against the desktop client Java version.
For an example server that needs 4 GiB heap, the start may look like below. Replace the name server.jar with your file. This is a syntax example and not a 4 GiB recommendation for any online use.
java -Xms2G -Xmx4G -jar server.jar --noguiCreate a budget for the process
Xms is the initial size of the heap, Xmx is its upper limit. In addition to heap, there is metaspace, thread stacks, buffers and other native costs. The Docker/cgroup limit applies to a larger set of memory than a single heap.
For example, with a container limit of 6 GiB and Xmx4G, there is about 2 GiB left for the rest of the process; this is just an initial hypothesis. Measure actual RSS, container memory, and behavior under load. The host also needs reserves for the OS, proxy, database, and backups. Don't add up the maximums of all the services as if they would never match.
Distinguish insufficient memory from normal GC
The sawtooth heap graph is normal: the memory fills up, then the collector frees some of the objects. The growing lower level after assemblies under the same load is suspicious, but even this requires an analysis of the objects being held.
OutOfMemoryError: Java heap space and killing the OOM killer/cgroup process are different cases. In the second, look for the event in the container/system log. If the heap is free and MSPT is high, increasing Xmx does not eliminate expensive generation or synchronous plugin code.
Separate game tick time from pauses and other costs. spark profile.
Change one parameter and repeat the load
Save the launch command and initial values. After one change, repeat the players' entry, generation of the necessary chunks, farm operation and saving. Compare not only free memory, but also pauses, MSPT and maximum container consumption.
Don't copy a long set of flags from an old build to a new Java without understanding. First, a working version of runtime, reasonable heap and measurement; then adjustment for the specific reason found.
The memory graph is more useful together with load and events. Set of server metrics.

