The major memory-accounting domains of a JVM process — heap, Metaspace/class space, code cache, thread stacks, direct/native/JVM-internal memory and mapped/file-backed pages — and how to budget them against a container limit. Use when a pod is OOMKilled with no Java exception, when an OutOfMemoryError names something other than "Java heap space", when -Xmx is set equal to the container limit, when RSS exceeds the heap by more than expected, when a heap above 32 GB is proposed, or when sizing ...
Installs into .claude/skills of the current project.
Are you the author of Jvm Memory Regions?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/robsonkades-jvm-memory-regions)
---
name: jvm-memory-regions
description: >
The major memory-accounting domains of a JVM process — heap, Metaspace/class space, code
cache, thread stacks, direct/native/JVM-internal memory and mapped/file-backed pages — and
how to budget them against a container limit.
Use when a pod is OOMKilled with no Java exception, when an OutOfMemoryError names
something other than "Java heap space", when -Xmx is set equal to the container limit,
when RSS exceeds the heap by more than expected, when a heap above 32 GB is proposed, or
when sizing a JVM for Kubernetes. Does not cover collector choice and heap tuning
(jvm-gc-tuning), classloader leaks (jvm-class-loading), or kernel-side memory behaviour
such as page faults, swap and the OOM killer (linux-for-jvm). Metaspace internals are
metaspace-internals, memory outside the heap is off-heap-memory, and heap contents are
heap-dump-analysis.
---
# JVM Memory Regions
## Purpose
Budget a JVM process against a hard memory limit. A common failure is `-Xmx` set to the
container limit, with non-heap
regions pushing RSS past the cgroup, and the **kernel** killing the process — no
`OutOfMemoryError`, no shutdown hook, no heap dump, because the JVM never knew it was
dying.
Only the Java heap object graph is reclaimed directly as ordinary GC-managed objects, but
class unloading, code-cache reclamation, native cleaners/arenas and OS reclaim couple the
other domains to different lifecycles. Not every domain has a hard flag or a distinct
`OutOfMemoryError`; that is why accounting starts from evidence rather than six fixed boxes.
Inspect the target JDK/vendor/update, collector, effective flags, OS and cgroup hierarchy
before applying a budget. JDK 25 examples describe one HotSpot environment; neither a newer
feature nor a measurement here authorizes changing the project's runtime.
## Workflow
Start from the requested decision and available deployment, runtime and workload evidence. A
flag explanation does not require a new recording, and a sound existing budget need not change.
Use the relevant steps below; a sizing request without a failure needs capacity evidence rather
than an invented OOM diagnosis. Treat an unknown limit or unobserved peak as an assumption to
resolve, not a measured value. Changes or restarts must fit the already authorized action scope.
1. **For a failure, read the full OOM message first.** It suggests an allocation path or resource limit, not necessarily
the root cause; preserve causes and external process/container evidence. `Metaspace`, `Direct buffer memory` and `unable to create native
thread` require different evidence; raising `-Xmx` is not a general repair and can increase
native/cgroup pressure even when it also raises an implicit direct-buffer limit.
2. **Measure JVM-tracked non-heap with NMT under your own load**, and model untracked domains:
`-XX:NativeMemoryTracking=summary` at start, then
`jcmd <pid> VM.native_memory summary`; `baseline` followed later by `summary.diff`
attributes tracked growth to a category. Periodic JFR NMT/RSS events can provide a
related time series when present/enabled, but do not assume identical semantics. The NMT
events also require NMT enabled at startup; starting JFR later cannot recover that missing
attribution. NMT cannot be enabled on a running process. When it is absent, use existing
metrics/maps and retain the attribution gap; restarting solely to complete this workflow
is not required. Near a hard limit, diagnostic clients and extra JVM probes consume scarce
capacity too; prefer existing telemetry and bounded OS reads until their overhead fits the budget.
3. **Budget every region and peak overlap**: candidate heap max = limit − measured/modelled
non-heap/cgroup peaks − uncertainty/recovery headroom. Then express it as `-Xmx` or
`MaxRAMPercentage` against the memory value the JVM actually detected, and verify the
resulting `MaxHeapSize`: small-memory ergonomics and alignment can change the percentage result;
the arithmetic and the RSS-versus-NMT gap table are in
`references/container-budget.md`.
4. **Distinguish virtual reserved, NMT committed, resident and cgroup-charged.** `ps` exposes
both VSZ and RSS; neither is identical to NMT totals or `memory.current`. A residual
obtained by subtracting NMT committed from RSS is not a measured untracked-native total.
5. **Judge normalized trends, not instants** — compare equivalent reclamation points,
native/RSS/cgroup peaks and the workload regime that produced them.
6. **Deliver a bounded decision.** State observed attribution, competing explanations,
the candidate budget or evidence for keeping the current settings, and material unknowns.
Separate proposed effects from validated results; name the next discriminating check or
rollout measurement. Missing NMT/maps or peak-overlap evidence remains a limitation.
## Rules
- Choose fixed versus variable initial heap from startup, RSS/density, idle-uncommit and SLO
measurements. `-Xms = -Xmx` removes heap growth but is not a universal production rule.
- Set `MaxMetaspaceSize` only as a deliberate fail-fast/capacity boundary. Too low causes
avoidable OOM during legitimate class loading; absent/unbounded shifts the boundary to
process/cgroup capacity. Alert on class-loader/class-space trends either way.
- `unable to create native thread` can mean PID/rlimit exhaustion, native allocation
failure, cgroup pressure or virtual-address constraints. Changing `-Xmx` helps only when
measured heap commitment/residency is consuming the relevant resource; it can also hide
the real PID/thread-lifecycle defect.
- The compressed-oop cutoff for applicable HotSpot collectors is often near 32 GiB but
depends on alignment, heap base/reservation and build. Confirm effective flags/layout;
ZGC's colored-pointer scheme is not a compressed-oops workaround.
- `-Xss × platform-thread count` is only an approximate Java-stack reservation model,
not a process-wide bound or resident bytes. JVM/native threads, main-thread policy,
rounding and guard regions can differ; inspect actual reservations. Reduce stacks only after testing Java/native call depth and guard-page behavior;
stack overflow is a correctness failure, not merely a tuning regression.
- Code-cache pressure can stop compilation and trigger reclamation/restart behavior
that varies by tier/segment and release. `jdk.CodeCacheFull` is strong evidence of an
event, not proof of permanent interpretation or a universal 80% threshold. Correlate
compiler logs, segment occupancy, reclamation and throughput (`code-cache-segments`).
HotSpot removed the sweeper in JDK 20; on the JDK 25 baseline, do not require a sweeper
thread or its old lifecycle states as evidence.
- Measure object layout with JOL rather than estimating headers. Compact object headers
(JEP 519, product in 25) are **off by default through JDK 26 and on by default from
JDK 27** (JEP 534); confirm the effective setting on the target build. Disable with
`-XX:-UseCompactObjectHeaders`. **Do not budget 8 bytes
per object**: alignment makes savings class/layout-dependent, and some common small
objects can retain the same aligned size while their surrounding graph/arrays change.
For the rule that predicts which classes do save, the measured
per-class table and the per-object arithmetic, see `object-layout-and-footprint`.
- Unmounted virtual-thread continuation chunks live in the heap, while mounted execution
uses carrier/native stack state. Millions of tasks can therefore shift context/stack
retention into GC-visible objects; measure mounted/unmounted state and in-flight
concurrency before adjusting heap or `-Xss`.
- `MaxDirectMemorySize` is a separate ceiling. On the verified HotSpot implementation its
absent value resolves to `Runtime.maxMemory()`, so choosing a large `-Xmx` can implicitly
authorize a similarly large direct-buffer budget; it does not reserve that memory or make
the combined process fit.
## Evidence and rollout safety
- Capture NMT, process maps, cgroup files and JFR at aligned timestamps with JDK vendor/
update, PID, container ID and load. “Committed,” RSS/PSS and `memory.current` must never
be joined from different windows as if simultaneous.
- Process maps, hs_err, heap/core dumps and command lines can expose paths, credentials and
payloads. Restrict collection/transfer, redact only on a preserved copy, record hashes and
expire artifacts under incident policy.
- For any limit/heap/stack/metaspace change, canary with abort thresholds for OOM kills,
Java OOMs, startup time, RSS/cgroup high-water mark, faults, GC tails, throughput and
stack overflow. Retain the previous configuration for rollback.
## References
- [Container budget](references/container-budget.md) — the per-region budget with the
worked `MaxRAMPercentage` arithmetic, reading `VM.native_memory summary` and `diff`,
the RSS-versus-NMT gap table, and the Kubernetes checklist. Read when sizing a JVM for
a memory limit or when RSS does not match the heap.
- [OOM triage by region](references/oom-triage.md) — which message means which region,
the `jcmd` command that confirms each, what does _not_ fix it, and the
`ExitOnOutOfMemoryError` / `CrashOnOutOfMemoryError` decision. Read when an
`OutOfMemoryError` or an OOMKill has already happened, or when configuring what the JVM
does on the next one.
Authoritative sources: [Oracle Native Memory Tracking guide](https://docs.oracle.com/en/java/javase/25/vm/native-memory-tracking.html),
[Oracle container support guide](https://docs.oracle.com/en/java/javase/25/docs/specs/man/java.html#java-options-for-linux),
[JEP 519](https://openjdk.org/jeps/519), [JEP 534](https://openjdk.org/jeps/534),
[JDK 27 release notes](https://www.oracle.com/java/technologies/javase/27all-relnotes.html),
[HotSpot sweeper removal, JDK-8290025](https://bugs.openjdk.org/browse/JDK-8290025), and the
[Linux cgroup v2 memory controller](https://docs.kernel.org/admin-guide/cgroup-v2.html#memory).