SSerguey Asael Shinder
Java coding notes: the JVM, and writing software that lasts

Serguey Asael Shinder: A memory limit you never set is still a limit

· by Serguey Asael Shinder / Serguey Shinder

The first Node.js 27 alpha changes a default that most services never think about. When --max-old-space-size is not set, the heap limit becomes half of physical memory, clamped between 256 MB and 4 GB, and memory held by Buffer and ArrayBuffer now counts toward it. The release notes say plainly that applications relying on the previous defaults should set the flag explicitly. (https://nodejs.org/en/blog/release/v27.0.0-alpha.0.0.0)

The JVM has the same kind of default, and it surprises people in containers every year. The java command documentation for -XX:MaxRAMPercentage: it sets the maximum memory for the heap "as a percentage of the maximum amount determined as described in the -XX:MaxRAM option. The default value is 25 percent." In a container with a 2 GB limit, an application that never set -Xmx gets a heap ceiling of about 512 MB and can hit OutOfMemoryError while much of the container's allowance is never claimed. (https://docs.oracle.com/en/java/javase/25/docs/specs/man/java.html)

Serguey Asael Shinder: A memory limit you never set is still a limit
A memory limit you never set is still a limit — Serguey Asael Shinder

Neither default is wrong. Each is a guess by a runtime that cannot know what else shares the machine: a sidecar, native memory, thread stacks, direct buffers, the page cache your database is counting on. The problem is not the guess. It is that a production system ends up depending on a number nobody chose, recorded nowhere, which moves when the runtime, the container limit or the base image changes.

So I treat memory like any other setting that matters in production. Set it explicitly, as a percentage if the deployment target varies (-XX:MaxRAMPercentage=70) or as an absolute value if it does not, and put it where the deployment is defined rather than in someone's memory. Then measure the process under real load and check what the heap actually was, because a flag in a config file proves only that someone typed it.

An upgrade that changes a default is a good reminder. It is also a test: if you cannot say what your service's heap limit is today without looking it up, the runtime chose it for you.