You can't patch what you can't find: building a real JVM inventory
Patchmageddon Team
JVM patching & OpenJDK migration specialists
Ask a platform team how many Java runtimes the company runs and you'll usually get a confident number. Ask where that number came from and the confidence drops. It's typically the CMDB, a vulnerability scanner report, or a spreadsheet someone built during the last Log4j scramble.
None of those is wrong, exactly. They're incomplete in ways nobody can measure. That's the problem, because every later step (patching, upgrades, license decisions, audit responses) depends on the list being right.
Why nobody has an accurate list
JVMs don't only show up as "a Java application we deployed." They arrive in several other ways, and most of those skip the processes that would record them.
Shadow JVMs inside vendor products
Commercial software often ships its own private Java runtime. Databases, monitoring tools, backup agents, BI platforms, network management consoles and security products regularly bundle a JRE in their install directory. It isn't on the PATH, it doesn't show up in the OS package manager, and the team that installed the product often doesn't know it's there.
These runtimes tend to be the oldest in the estate, because they're only updated when the vendor ships a new release. We cover how to handle them in patching JVMs inside vendor products.
Application servers
Tomcat, WebLogic, WebSphere and JBoss/WildFly installations usually point at a specific JDK through a startup script or environment file, not the system default. A server can have a patched JDK installed next to it while still running an old one.
Containers
Every container image that includes Java carries its own runtime, fixed at build time. Patching the host changes nothing inside the container. When a base image is rebuilt rarely, or a team copied an old FROM line years ago, the vulnerable runtime ships to production on every deploy.
Build agents and desktop tools
CI runners, build agents, developer workstations and desktop tools such as IDEs, database clients and internal Swing apps all carry JVMs too. They're often left out of scope because "they're not production." They still count toward license exposure, and build agents are an attractive target for attackers.
What a useful inventory captures
A list of hostnames with "Java: yes" doesn't help much. To drive decisions, each record needs:
| Field | Why it matters |
|---|---|
| Every running instance, across dev, staging and prod | Non-production environments still count for licensing and are still attack surface |
| Version, vendor/distribution and patch level | Oracle JDK vs. Temurin vs. Corretto vs. Zulu changes both licensing and the patch source |
| Where it's embedded | Standalone app, app server, vendor product, or container image, since each is patched a different way |
| CVE exposure, scored per JVM | A risk score per runtime, adjusted for exposure (such as internet-facing), not just a raw CVE count |
| Owner | The person or team who can approve a change. Often the hardest field to fill in |
| Installed agents | APM, security and profiling Java agents are a leading cause of patch failures |
| Criticality signal | Enough to seed a proper risk classification later |
The last two fields are often skipped, and they're what turn an inventory into a patch plan. Knowing a JVM runs an APM agent tells you to test that agent before rollout. A criticality signal tells you which process the app should go through (see our approach).
How to collect it
No single source is complete. The reliable approach combines several and reconciles them.
Agentless host scanning
Over SSH (Linux/Unix) or WinRM (Windows), with a read-only account, you can:
- List running processes and capture the full path of each
javaexecutable - Search the filesystem for Java installations, including ones that aren't running
- Read each installation's
releasefile, which most modern JDKs and JREs include and which records the version and implementor
Here is a simple, read-only example for a Linux host:
# Running JVMs: resolve the actual binary behind each java process
for pid in $(pgrep -x java); do
bin=$(readlink -f "/proc/$pid/exe" 2>/dev/null)
echo "running pid=$pid bin=$bin"
done
# Installed JVMs: find 'release' files next to a bin/java, then print key fields
find / -xdev -type f -name release -path '*/release' 2>/dev/null |
while read -r f; do
dir=$(dirname "$f")
[ -x "$dir/bin/java" ] || continue
echo "== $dir"
grep -E '^(JAVA_VERSION|IMPLEMENTOR|JAVA_RUNTIME_VERSION)=' "$f"
done
Older runtimes, Java 8 and earlier in particular, may not have a release file or may record fewer fields. For those, running "$dir/bin/java" -version is the fallback. It executes the binary, so decide deliberately whether your access policy allows that.
CMDB and configuration-management reconciliation
Compare what you found with what your CMDB and your Ansible, Puppet or Chef data say should be there. The differences are often as valuable as the inventory itself: runtimes nobody registered, records for servers decommissioned years ago, and hosts where configuration management thinks it installed JDK 17 but something else is actually running.
Cloud provider APIs
AWS, Azure and GCP APIs list instances, managed container services and serverless functions, along with tags that often point to an owner or cost center. Serverless runtimes managed by the provider are a different conversation, but you still need to know they exist.
Container image scanning
Scan the images in your registries and the images actually running in your clusters. Registry scanning finds what could be deployed. Cluster scanning finds what is deployed, including images from registries nobody remembers configuring.
Artifact repository scanning
Nexus and Artifactory hold the build outputs that become deployments. Scanning them finds embedded runtimes, such as a bundled JRE inside a distribution archive, before they're running anywhere.
Reuse what your scanners already know
If you run a general-purpose vulnerability scanner, it has already seen many of these hosts. Use its data as an input rather than competing with it. What it usually can't do is interpret the results for Java specifically: which runtime a finding belongs to, whether it's embedded in a vendor product, what the patch path is, and what the licensing implication is.
What the output looks like
To make that concrete, here's an illustrative example, not a real client result. A mid-sized estate assessment might report:
- 340 JVMs discovered, of which 118 were missing from the CMDB
- 60 past end of public support
- 12 with critical CVEs open for more than 400 days, several on internet-facing hosts
- 38 Oracle JDK installations, which under Oracle's employee-based subscription can bring the whole headcount into license scope (see the employee metric explained)
Numbers like these do what a policy memo can't: they give security, platform and finance a shared, specific reason to act.
Where discovery goes wrong
Ephemeral workloads
Autoscaling groups, short-lived containers and batch jobs make "inventory" a moving target. A point-in-time scan will miss some of them. Decide early whether you're counting instances, images or both. Scanning images and deployment definitions usually gives a more stable picture than chasing instances.
Ownership needs people
Scanning can tell you what's running. It rarely tells you who's accountable for it. Tags, CMDB records and cost centers get you part of the way. The rest takes interviews and follow-up, so budget time for it instead of assuming it can be automated.
Access and data handling
Getting even read-only access across a fragmented estate is a procurement and security exercise in its own right. Ask for the minimum privilege that works, document exactly what's collected, and agree up front on data retention and handling. This matters most in regulated environments.
What to do next
If you don't trust your current count, start with a scoped discovery of one business unit and compare it to what your records say. Our JVM discovery and estate assessment does exactly that. Once you have the inventory, drift monitoring keeps it current as the estate changes.
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
Oracle JDK to OpenJDK: a practical migration checklist
A step-by-step checklist for moving from Oracle JDK to an OpenJDK build: choosing a distribution, what differs between vendors, verification and cleanup.
How to get your CAB to approve faster JVM patching
Faster JVM patching is mostly a governance problem. How to score criticality, set thresholds and build the evidence a Change Advisory Board can approve.