Which JDK Are You Actually Running

JDK 17 shipped nine days ago under a license that permits production use at no charge, three years after Oracle started charging for the same build. This is what that does and does not settle, starting with a free period bounded by Oracle's release calendar rather than by a date. The expensive half of leaving Java 8 was never the license anyway, and it is forty libraries moving across their own breaking changes.

The Question Nobody Can Answer

Ask a room of engineers which JDK their production systems run and watch what happens. Somebody says 11. Somebody else says 8, and is right about a different service. Almost nobody names a vendor, because for about fifteen years there was only one answer worth naming and it came preinstalled. Run java -version on any of those machines and the second and third lines tell you who built the runtime and under what license, and those two lines are the ones nobody has read.

Not knowing was perfectly rational for most of that time, because the question had no consequences attached and habits that never form do not appear on any list of things you lack. The consequences arrived with JDK 11 on the twenty-fifth of September 2018, when Oracle shipped its build under a license requiring a paid subscription for production use. What followed was a scramble: the same code, compiled and tested by a dozen other organizations and published free, and a set of teams discovering that "which JDK" now had an invoice behind it. Some answered the question. Rather more stayed on 8.

Then, nine days ago, Oracle reversed. JDK 17 shipped on the fourteenth of September under a new No-Fee Terms and Conditions license permitting commercial and production use at no charge. That is genuinely good and it puts nothing back the way it was, because the free period has a defined end written into its terms. The vendor of your runtime has stopped being a default and become a decision with a review date. And while everybody was arguing about that, the part of the move off Java 8 that actually consumes a schedule sat untouched, somewhere else entirely.

Why the Builds Are the Same

They are the same source. Oracle, Eclipse, Amazon, Microsoft, Red Hat, SAP, Azul, BellSoft, IBM and Alibaba all publish builds compiled from the OpenJDK repository, and for a long-term release they track the same updates project. Vendor differences are build configuration, patch backports and packaging, applied on top of a tree everybody shares. That is not a claim about quality; it is a statement about provenance, and it is why swapping vendors is not a migration.

Compatibility is a test suite, not a promise. The Java SE Technology Compatibility Kit turns "compatible" from marketing into something with a pass or a fail. When Eclipse published its first Temurin release on the second of August this year, covering Java SE 8, 11 and 16, it stated that every release passes the Oracle Java SE TCK as well as the Eclipse AQAvit suite. That is the sentence to look for on any vendor's page, and its absence is the only interesting finding available there.

What differs is the calendar and the packaging. Vendors publish different end-of-support dates for the same version, differing by years rather than months, and they differ in container images, installer formats and which architectures exist. Neither of those changes how your program runs and both change how your pipeline works. So picking a build is picking how long somebody keeps issuing security updates and how much friction your images inherit, which is a procurement question with a calendar answer.

A License With an Expiry

The No-Fee Terms and Conditions license published alongside JDK 17 permits use of Oracle's own build for commercial and production purposes at no cost, which is the thing that was not true for JDK 11 through 16. The important detail is not that it is free. It is how the free period is bounded: Oracle defines it relative to the arrival of a later long-term release rather than as a fixed date, so the moment your usage stops being free is a function of a schedule Oracle publishes and can revise.

That is a different kind of obligation from the one people are used to, and worse in one specific way. A contract with an end date can be diarized by anyone who reads it once. A window that closes some period after an event that has not happened yet cannot be diarized at all, which means the review has to be recurring and owned by a person rather than filed in a renewal calendar. Nobody sets that up for something they obtained by downloading it. And this is the second license change to the same artifact from the same vendor inside three years, which I read less as bad faith than as evidence about what kind of thing a runtime license is.

How much any of it costs you turns on something unrelated to Java. If everything runs in containers you build from a base image you chose, this is twenty minutes of reading and a line in a policy document. If there is Java on employee laptops, inside a build server somebody set up in 2016, and embedded in three vendor appliances whose installers laid down a runtime without telling anyone, then it is an inventory problem, and inventory is the part that costs money. The license is cheap. Finding out is not.

Where the Money Actually Goes

The platform got modular even though your code did not. Nothing forces an application onto the module system, and this is the most common misreading of the whole transition. The breakage does not come from adopting modules; it comes from the fact that the JDK itself is now modular, so the internals your dependencies used to reach into are behind a boundary that is enforced. You are not being asked to modularize. You are being told that the platform did, and that the consequences are yours.

The Java EE modules left, and they left quietly. JEP 320 removed the bundled Java EE and CORBA modules in JDK 11, which means anything calling into javax.xml.bind or its neighbors now needs those artifacts declared as ordinary dependencies. The failure is a NoClassDefFoundError at runtime rather than a compile error, so it surfaces in whichever code path runs least often, which in practice is an error handler or a nightly job.

Strong encapsulation stopped being negotiable. JDK 16 made denial the default for reflective access into JDK internals, and JEP 403 in JDK 17 removed the command-line option that let you relax it. Libraries that reach into internals - serialization frameworks, mocking tools, bytecode generators - need versions that stopped doing so. The --add-opens escape hatch still exists per package, and every use of it is a debt you should record somewhere a person will read, because it is a list of things that will break again.

The garbage collector changed underneath you. G1 became the default in JDK 9, replacing the parallel collector Java 8 used, and the concurrent mark sweep collector was removed outright in JDK 14. So a service carrying heap flags tuned in 2016 does not fail to start. It starts, ignores the flags it no longer recognizes with a warning nobody reads, and runs with a different pause and throughput profile than the one your capacity planning assumed. That is a performance regression with no error attached to it.

The version string changed shape in JDK 9. Java 8 reported itself as 1.8.0_292 and everything since reports a plain major version, and there is a startling amount of code in the world that parses the old form. It is rarely in the application. It is in a shell script that gates a deployment, a monitoring agent, an installer check, or a library's feature detection, and it is always owned by somebody who has left. This one is trivial to fix and hard to find, which is the worst combination for a migration plan.

A Dependency Upgrade in Disguise

Java 17 produces class files at major version 61, and every tool that reads or writes bytecode has to understand that number before anything using it will run. That set is larger than it sounds: the mocking framework, the object-relational mapper's proxy generator, the dependency injection container, the coverage tool, the aspect weaver, and whatever the build plugin chain drags in transitively. Each needs a version that supports 17, and several of them changed their own APIs to get there.

Then the things that attach to a running JVM. Profilers, application performance agents and runtime security agents hook into internals by design, they are all version-coupled, and they are exactly the tools you would want pointed at a freshly upgraded service. The ordering is uncomfortable: observability is usually the last thing certified for a new release, so the period when you most need to see inside the process is the period when you can see least.

So the honest description of an 8-to-17 project is that it is mostly a dependency upgrade. You are not writing much Java; you are moving forty libraries across their own breaking changes, which means the schedule is set by the least well maintained thing in your tree rather than by anything about the language. Whether you stop at 11 on the way barely matters, because the breakage is cumulative either way. Teams estimate the whole thing as a runtime change and are then surprised by a quarter of library archaeology, which is the one prediction in this piece I would make with confidence.

I want to be careful not to turn this into a complaint about a vendor, because the last three years read differently depending on where you stood. Oracle funds an enormous amount of the work that produces the thing everybody ships, the 2018 change was a legitimate attempt to charge for something with real costs behind it, and the reversal this month is a straightforwardly good outcome for everyone downloading a runtime. What the episode changed is not who to trust. It is that a property of the platform which used to be constant is now something with a version number and a review date.

The habit worth keeping out of it is smaller than any of the arguments. For fifteen years the answer to "which JDK are you running" was so obvious that nobody built the reflex to check, and the cost of not having that reflex was paid in the two years afterward, by people reading license terms under time pressure while trying to work out what a nightly job in a subsidiary had been started with. The license is free again. The inventory is still the thing you do not have.