skip to content

What do jcmd's VM.flags and VM.system_properties tell you, and how would you use them to debug a misconfigured production JVM?

level: middleimportance: should knowfreq 45%

answer

  1. VM.flags = effective -XX options; -all = every flag incl. defaults
  2. VM.system_properties = java.* built-ins + your -D values
  3. Effective config can differ from intended (ergonomics/container/script)
  4. Check MaxHeapSize, UseXxxGC, ActiveProcessorCount
  5. Replaces legacy jinfo; pairs with VM.command_line

basics

~20 s

VM.flags shows the JVM's -XX options actually in effect (heap size, GC, etc.). VM.system_properties shows the Java system properties (like java.version or user-set -D values). Together they let you confirm what the running JVM is really configured with versus what you intended.

solid answer

~50 s

'jcmd <pid> VM.flags' prints the JVM's -XX flags currently in effect; add '-all' to see every flag including defaults, not just the ones you set. This is the fast way to confirm the real, *effective* configuration of a running JVM — max heap (-XX:MaxHeapSize), chosen collector (e.g. UseG1GC), and any ergonomically-derived values the JVM picked itself. 'jcmd <pid> VM.system_properties' dumps the Java system properties: built-ins like java.version, java.home, os.name, plus anything passed as -Dkey=value. The classic debugging use: a service behaves as if a flag or property didn't apply — you suspect a typo, wrong launch script, or container-derived default. You attach with jcmd and read what the JVM *actually* has, rather than trusting the intended command line. For example, confirming the container saw the right CPU/memory limits (and thus sized the heap as expected), or that -Dspring.profiles.active really reached the process. These replace the legacy jinfo tool.

go deeper

for a junior

Knows VM.flags lists JVM tuning options and VM.system_properties lists java.* properties and -D values.

for a middle

Uses both to verify a running JVM's real configuration and explains effective-vs-intended config and ergonomics.

for a senior

Debugs container/ergonomic mis-sizing (heap, processor count) from flags, correlates with VM.command_line, and knows the jinfo replacement story.

for a principal

Bakes config-verification (and explicit flag setting to avoid ergonomic surprises) into deployment standards and incident runbooks across the fleet.

## The problem they solve The configuration a JVM *runs with* is not always the configuration you *think* you gave it. A launch script may be overridden, a container may inject defaults, the JVM's **ergonomics** (its automatic sizing based on detected CPU/RAM) may pick values you didn't, and a `-D` property may be silently dropped by a quoting bug. jcmd lets you read the *effective* state directly from the live process — the source of truth. ## VM.flags — the -XX options in effect ``` jcmd <pid> VM.flags # the flags that differ from default / were set jcmd <pid> VM.flags -all # EVERY flag and its current value (long output) ``` **-XX flags** are the JVM's tuning knobs. Examples you'll see: - `-XX:MaxHeapSize=4294967296` — the real max heap (often derived from container memory, not what you assumed). - `-XX:+UseG1GC` / `-XX:+UseZGC` — which **garbage collector** is active. - `-XX:MaxMetaspaceSize`, `-XX:+HeapDumpOnOutOfMemoryError`, `-XX:ActiveProcessorCount` — and many more. `VM.flags` (no `-all`) shows what's *non-default* / set — concise and usually what you want. `-all` dumps the entire flag table including manageable/diagnostic flags, useful when you need to confirm a default value the JVM ended up with. Crucially, this reflects **ergonomic** decisions: in a container with a 2 GB limit, the JVM may pick a ~512 MB heap on its own; `VM.flags` shows the *actual* number. ## VM.system_properties — the Java properties ``` jcmd <pid> VM.system_properties ``` **System properties** are the `java.lang.System.getProperty(...)` key/value pairs. They include JVM built-ins — `java.version`, `java.home`, `os.name`, `user.dir`, `file.encoding` — and everything you passed as `-Dkey=value` (and what frameworks set, e.g. `spring.profiles.active`). This is how you confirm that a `-D` actually reached the process and has the value you expect. ## Putting them to work — debugging a misconfigured JVM Typical scenarios: 1. **"Why is the heap so small / OOMing?"** — `VM.flags` reveals `MaxHeapSize` is far below intended because the container memory limit (or a missing `-Xmx`) led ergonomics to a small heap. You see the real number instead of guessing. 2. **"We set the G1 collector but pauses look like the old one."** — `VM.flags` shows which `Use...GC` is actually true; maybe a later script clobbered the flag. 3. **"The wrong Spring profile is active."** — `VM.system_properties` shows the actual `spring.profiles.active`, proving the `-D` was mistyped or never passed. 4. **"Which JDK is this really running?"** — `java.version` / `java.home` in the properties confirm the runtime, handy when multiple JDKs are installed. 5. **"Did the container detect the right CPU count?"** — `ActiveProcessorCount` in flags drives default thread-pool and GC-thread sizing; a wrong value (cgroup misdetection) silently mis-sizes everything. ## Why via jcmd These replace the legacy `jinfo` tool. Reading from the *running* process avoids the trap of trusting the intended command line: ergonomics, container injection, and script overrides all mean the effective config can differ from the written one. (Related: `VM.command_line` shows the actual launch line, and `VM.version`/`VM.uptime` round out the introspection set.)

  • A containerized service OOMs even though you 'set 4 GB'. How does jcmd help find the cause quickly?
    Run 'jcmd <pid> VM.flags' and read MaxHeapSize and ActiveProcessorCount. Often the container memory limit (or a missing -Xmx / wrong MaxRAMPercentage) made ergonomics pick a much smaller heap than intended; the flag shows the real value so you can fix the limit or set -Xmx explicitly.
  • What's the difference between VM.flags and VM.command_line?
    VM.command_line shows the literal launch arguments the process was started with; VM.flags shows the resulting effective -XX flag values, including those the JVM derived ergonomically that never appeared on the command line.

saying these in an interview costs you the question

  • Trusting the launch script instead of reading the live process
  • Forgetting -all when you need a default-valued flag
  • Confusing -XX flags (VM.flags) with -D system properties (VM.system_properties)
  • Assuming heap size equals -Xmx when ergonomics derived it from container memory

context