Six ways JVM changes break production, and how to see them coming

APM agents, TLS defaults, JIT warm-up, GC changes, removed APIs and container awareness: how each JVM failure mode shows up, and how to catch it before rollout.
Patchmageddon Team

Patchmageddon Team

JVM patching & OpenJDK migration specialists

·6 min read

When a JVM change breaks something in production, it rarely fails in a new way. Most incidents fall into a short list of patterns that repeat across organizations and releases. If you know the list, you can check for each item before rollout instead of discovering it afterwards.

First, though, it matters what kind of change you're making.

Not every "JVM patch" carries the same risk

Failure modeSame-major security update (e.g. 17.0.x → 17.0.y)Major version upgrade (e.g. 11 → 17 → 21)Vendor swap, same version (e.g. Oracle JDK → Temurin)
APM and instrumentation agentsOccasionallyFrequentlyOccasionally
TLS and cipher defaultsOccasionallyFrequentlyRarely
JIT and warm-up behaviorRarelyOccasionallyRarely
GC defaults and flagsRarelyFrequentlyRarely
Removed APIs and encapsulationNoFrequentlyNo
Container awarenessOnly on very old buildsOccasionallyOnly on very old builds

The frequencies are our qualitative judgment, not measured rates. The pattern is what matters: same-major security updates are low-risk, and most serious breakage comes with major upgrades. That's why they should be planned and governed differently (see our approach).

1. APM and instrumentation agents

APM, security and profiling tools usually attach to the JVM as Java agents (-javaagent) and rewrite bytecode as classes load. To do that they have to understand the class file format and, often, parts of the JDK's internals.

Symptom: The application fails to start, logs errors from the agent, or runs normally while the agent silently stops reporting. On major upgrades, an older agent may simply not support the newer class file version.

Detect before rollout:

  • Inventory which agents, and which versions, are attached to each JVM. Check the startup command line and environment variables such as JAVA_TOOL_OPTIONS
  • Check each vendor's supported-JDK matrix for the target version
  • In the canary stage, gate on "the agent is reporting," not just "the app is up"

Mitigate: Upgrade the agent before, or together with, the JVM. For major upgrades, test the agent on the target JDK first.

2. TLS and cipher suite defaults

JDK updates regularly tighten security defaults: disabling old protocol versions, removing weak cipher suites, and distrusting certain root certificates. A notable example: TLS 1.0 and 1.1 were disabled by default across supported Java releases in the April 2021 updates.

Symptom: Handshake failures when connecting to older partners, appliances, databases, mainframe gateways or internal services still using old protocols or ciphers. These often appear only in specific integrations, which is why they slip through generic testing.

Detect before rollout:

  • Read the release notes for security-default changes. They're usually listed explicitly
  • Inventory the outbound TLS endpoints each application talks to, and test handshakes against them from a patched JVM
  • Watch handshake failure rates during the canary stage

Mitigate: Fix the far end where you can. Re-enabling a disabled protocol in java.security is sometimes necessary as a stopgap. Make it a documented, time-limited exception, not a silent config change.

3. JIT compilation and warm-up

The JIT compiler improves between releases, but "different" isn't always "better" for every workload. Changes in inlining, compilation thresholds or tiered compilation can shift how quickly an application reaches steady-state performance.

Symptom: Higher latency for the first minutes after startup, or a changed p99 on latency-sensitive services. Startup-time SLOs and autoscaling behavior are affected first.

Detect before rollout: Measure time-to-steady-state and tail latency on a canary under real traffic, not only in a quiet test environment.

Mitigate: Usually no action is needed for same-major updates. For latency-critical services on major upgrades, plan a performance check and consider warm-up traffic before instances join the load balancer.

4. GC defaults and tuning flags

Garbage collector defaults and options change across major versions. G1 has been the default collector since JDK 9. The CMS collector was deprecated in JDK 9 and removed in JDK 14. Many tuning flags accumulated over years of Java 8 operations are deprecated, ignored or rejected on newer releases.

Symptom: The JVM refuses to start because of an unrecognized option, or it starts and ignores a flag, changing memory and pause behavior. Pause times, heap usage or throughput shift after the upgrade.

Detect before rollout:

  • Collect the actual JVM flags from every instance during discovery, not just from documentation
  • Validate flags against the target version before rollout
  • Compare GC pause time and frequency between canary and baseline

Mitigate: Clean up flags as part of the upgrade. For most applications on modern JDKs, fewer hand-tuned GC flags is better than carrying old ones forward.

5. Removed APIs and strong encapsulation

This is the main source of breakage on major upgrades. Examples:

  • Strong encapsulation of JDK internals: JDK 16 (JEP 396) enabled strong encapsulation by default, and JDK 17 (JEP 403) removed the command-line option that relaxed it globally. Libraries that reached into sun.* and other internal packages via reflection can fail
  • The Security Manager: deprecated for removal in JDK 17 (JEP 411) and permanently disabled in JDK 24 (JEP 486). Applications and frameworks that rely on it need a different approach
  • The Nashorn JavaScript engine: removed in JDK 15. Applications that evaluated JavaScript through the scripting API need a replacement
  • Java EE modules such as JAXB and JAX-WS were removed from the JDK in JDK 11 and must now be added as explicit dependencies

Symptom: InaccessibleObjectException, ClassNotFoundException or NoSuchMethodError at startup or, worse, only on a rarely used code path.

Detect before rollout: Static analysis finds most of this before anyone patches anything. The JDK's own jdeps tool (including jdeps --jdk-internals) reports use of internal APIs, and jdeprscan reports use of deprecated APIs. Check library versions against their documented JDK support.

Mitigate: Upgrade the offending libraries. Use targeted --add-opens flags only as a documented, temporary exception.

6. Container awareness

Early Java 8 builds sized the heap and thread pools from the host's resources, not the container's CPU and memory limits. Proper container support arrived in JDK 10 and was backported to Java 8 in update 191 (8u191).

Symptom: Containers killed for exceeding memory limits, or JVMs using far more GC and compiler threads than the container has CPUs.

Detect before rollout: Discovery should flag any Java 8 runtime older than 8u191 running in a container. These are also, almost by definition, badly out of date on security patches.

Mitigate: Update the runtime. Then review any hard-coded -Xmx values set as a workaround, and prefer percentage-based sizing such as -XX:MaxRAMPercentage.

Turning the list into a checklist

For each application, before a JVM change:

  1. Which agents are attached, and do they support the target version?
  2. What outbound TLS endpoints does it use, and do they work against the new defaults?
  3. Is it latency-sensitive enough to need a warm-up check?
  4. Which JVM flags are set, and are they all valid on the target?
  5. For major upgrades: what do jdeps and jdeprscan report?
  6. If containerized: is the runtime container-aware?

Most of these questions are answered by a good inventory. See finding every JVM in your estate. The rest are covered by canary gates, described in fast detection and rollback.

What to do next

For same-major security updates on low-criticality apps, Rapid Patch builds these checks into the rollout. For business-critical systems and major upgrades, Critical Patch Assurance includes a readiness assessment that runs this analysis before any patch is attempted. If you're also changing vendors, see the Oracle JDK to OpenJDK migration checklist.

#jvm-upgrade#failure-modes#tls#garbage-collection#java-agents#testing
Patchmageddon Team

Patchmageddon Team

The Patchmageddon team helps enterprises find every JVM in their estate, patch it on a sane cadence, and stop paying for Oracle Java they don't need.

Find out what's actually running in your estate

A Quick Scan inventories the JVMs in one business unit in days. You get versions, vendors, patch levels, CVE exposure and Oracle licensing exposure, with no production changes and no CAB ticket.

More on Patch Management

Patch Management

Fast detection and rollback: a safer alternative to full regression testing

For low-criticality apps, quick detection and cheap rollback cut JVM patch risk more than a full regression run. Here's how to build that safety net.
5 min read