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
-Xmxis not automatically a leak; live-set trend after comparable collections matters. - A successful
jcmdcommand 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.
Related reading
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
- Java SE 25 Troubleshooting Guide
- Java SE 25 Diagnostic Tools
- Java SE 25 jcmd specification
- JDK 25 release definition
Official sources accessed August 19, 2026. Recheck the selected JDK vendor, update release, operating system, and command impact before using a diagnostic action.