Java Concurrency & Async Programming · Lesson 15

Production JVM Performance Diagnostics Explained

Diagnose production JVM performance from user impact through CPU, garbage collection, memory, threads, containers, and durable outcomes.

Quick answer

Production JVM diagnosis is an evidence-routing problem before it is a tuning problem. Start with the affected user operation and time window, confirm which Spring order service version and instances handled it, and then compare service latency, errors, CPU, allocation, garbage collection, Java heap, process memory, threads, container limits, dependencies, and durable order outcomes. No single JVM graph proves the cause or recovery.

Java 25 provides jcmd, unified JVM logging, Java Flight Recorder, heap inspection, native memory tracking, and virtual-thread-aware dumps. Each source sees a different boundary. Use the lowest-impact evidence that can distinguish the next hypotheses, preserve timestamps and version identity, and require explicit approval for captures that can pause the JVM or retain sensitive data.

Learning objectives

  • Build a symptom-to-evidence map that separates CPU, garbage collection, heap, native memory, thread, container, and dependency failure modes.
  • Move from a bounded hypothesis to a reversible mitigation and independent recovery evidence without treating a diagnostic command as production proof.

Prerequisites

Understand basic JVM memory, Java threads, Spring request handling, and the evidence limits in Production Java Concurrency Troubleshooting. Review JVM Profiling and JFR before collecting a recording.

Evidence and system boundary

Define the incident in user terms: checkout latency, rejected orders, ambiguous timeouts, or failed status retrieval. Bind that impact to UTC timestamps, route templates, service version, instance, container, and deployment change. Metrics aggregate a population; traces sample request paths; logs record selected events; JFR observes one JVM; thread dumps show stacks at capture time; heap dumps show object reachability; operating-system and container evidence show process constraints.

The following is an explanatory inventory, not a command sequence executed against a real system:

user outcome -> service SLI -> instance and version -> JVM signal -> OS/container signal -> dependency -> durable order state

CPU saturation may come from application work, garbage collection, JIT compilation, serialization, TLS, or a busy neighboring process. A rising heap can reflect a normal larger live set, delayed collection, burst allocation, or a leak. Many blocked threads can be a database wait rather than a monitor problem. Container termination can occur without a Java OutOfMemoryError. Keep these hypotheses separate until evidence connects them.

Build the diagnostic sequence

First preserve current state: deployment digest, JVM version and flags, container resource configuration, recent changes, request volume, and the affected business interval. Second, choose a signal that can divide the hypothesis set. GC logs distinguish collector activity from non-GC CPU; a class histogram separates dominant object populations; a thread dump identifies owners and waits; Native Memory Tracking separates HotSpot categories; JFR connects CPU, allocation, locks, I/O, and compilation inside one recording window.

Estimate capture impact before collecting. jcmd <pid> VM.version and VM.flags are low-impact context. A heap histogram can be high impact depending on heap size. A heap dump can request a full collection, pause the process, consume substantial disk, and expose credentials or personal data retained in objects. A profile configuration is bounded evidence, not permission to record indefinitely.

Record negative evidence carefully. An absent metric can mean no work, an instrumentation gap, an exporter failure, retention, or a query error. A cleared alert can reflect a repaired query rather than a recovered service. Compare an independent user check and durable order records before closing the incident.

Production failure scenario

Checkout p99 rises after a rollout while average CPU stays moderate. One dashboard shows frequent young collections, but pause time remains small. Thread evidence shows request work waiting for database connections, and traces show longer inventory queries. Process RSS is stable and the heap returns to a consistent live set after collection.

The team does not tune GC because collections are visually frequent. It bounds admission, investigates the changed query and pool ownership, rolls back the version, and watches fresh checkout outcomes, pool wait, database latency, GC time, retries, and durable order state. The JVM evidence rules out one attractive hypothesis; it does not prove the database change alone caused every failure.

Common misconceptions

  • High CPU does not identify application code; GC, JIT, kernel work, and neighboring workloads also consume CPU.
  • Frequent GC does not imply harmful GC if pauses, concurrent work, throughput, and user latency remain within objectives.
  • A heap near -Xmx is not automatically a leak; live-set trend after comparable collections matters.
  • A successful jcmd command proves only that the command returned, not that the service recovered.
  • One fast local benchmark does not represent production traffic, warmup, dependencies, or container contention.

Decision checklist

  • State the affected user operation, window, version, and durable outcome.
  • Preserve JVM, container, deployment, and traffic context before changing flags.
  • Choose the least disruptive evidence that separates competing hypotheses.
  • Record capture impact, storage, access, redaction, and retention.
  • Compare JVM evidence with OS, container, dependency, and business evidence.
  • Change one owned variable with a rollback path.
  • Verify fresh user outcomes and ambiguous durable state after mitigation.
  • Keep the incident open when telemetry freshness or evidence delivery is uncertain.

Continue with JVM Garbage Collection Logs and Pause Diagnosis, then follow the ordered Java Concurrency learning path and Java Concurrency topic cluster. Spring operators can connect the method to Production Spring Boot Incident Troubleshooting.

Official sources

Official sources accessed August 19, 2026. Recheck the selected JDK vendor, update release, operating system, and command impact before using a diagnostic action.

Knowledge check

Check your understanding

Answer both questions correctly to mark this lesson as mastered. You can retry without penalty.

1. Checkout p99 rises while young-GC frequency increases but pause time and live set remain stable; what should the team do first?

2. A JVM metric returns to normal after rollback, but timed-out orders have unknown outcomes; when is recovery established?