Your GraalVM Version Is the Other Number
Contents
This was going to be a buyer's guide: which GraalVM to build native images with in 2026, what the free edition lacks, and what Oracle's licence allows. It starts with a version check, because the to-do list for my benchmark's native builds opened with an upgrade. The builder was on GraalVM CE 25.0.4.1, the list said, a few releases behind the current line.
The build log has an opinion on that:
#4 [internal] load metadata for ghcr.io/graalvm/graalvm-community:25i4-25.0.4.1.1@sha256:a7b4810d7c755e9627feaa1459eb5a93338643b16d745d4f3fc86db71e5da7f5
[…]
#16 27.57 - Java version: 25.0.4.1.1+1, vendor version: GraalVM CE 25.4.4.1.1+1.1
Two version numbers share one line: Java version: is the JDK, vendor version: is GraalVM. The image is GraalVM CE 25.4.4.1.1 (22 September), the newest CE release when the benchmark switched to it, and every native row since the 17 October run came out of it. The recommended upgrade was already installed. The version it was based on was the JDK's, minus a digit.
I can't be smug about it. For two days in October my own talk slides carried the same number on the first slide, in a line that also promised every number in the deck had been measured, not quoted.
Once the two numbers are pulled apart, the rest of the guide gets simpler.
TL;DR —
- Read the GraalVM version from the build log or
$JAVA_HOME/release, never from the image tag. In25i4-25.0.4.1.1the train is25i4and the rest is the JDK; GraalVM 25.4.4.1.1 is not in the tag. - Free CE binaries on the 25.0 line stopped at 25.0.2 (20 January 2026). Patched CE now means a monthly innovation release; a patched 25.0 line means Oracle GraalVM, Mandrel or Liberica NIK.
graalvm-community:25still served 25.0.2 on 25 October. - CE 25.1+ already ships the new defaults: Serial
Adaptive2and compressed references for memory, priority inlining for speed. - Oracle GraalVM, free under its GFTC licence, adds PGO, GraalNN
-O3, G1 and an embedded SBOM: CPU, pause-time and supply-chain levers. I found no published measurement of any of them at 128–256 MB. - The footprint guide's 6× G1-vs-Serial memory gap is an uncapped heap on a 48 GB host. Capped at 512 MiB, G1 used 0.94× Serial's RSS and 41 % more CPU.
Two version numbers in one GraalVM string#
On the old builder, the September build on graalvm-community:25.0.2 printed Java version: 25.0.2+10, vendor version: GraalVM CE 25.0.2+10.1, the same number twice. On the 25.0 line GraalVM wore the JDK's version, so tag, JDK and product all read 25.0.2.
The release calendar puts GraalVM on JEP 223's MAJOR.MINOR.SECURITY scheme:
The MINOR number indicates the feature release train, such as 25.1, 25.2, and so on. The SECURITY number indicates the CPU level of the underlying JDK update.
The three GraalVM installs in my SDKMAN directory, ~/.sdkman/candidates/java, cover all the cases:
$ grep -E '^(GRAALVM_VERSION|JAVA_VERSION)=' *graal*/release
25.0.2-graalce/release:JAVA_VERSION="25.0.2"
25.0.2-graalce/release:GRAALVM_VERSION="25.0.2"
25.3.4+1.r25-graal/release:JAVA_VERSION="25.0.4.1"
25.3.4+1.r25-graal/release:GRAALVM_VERSION="25.3.4.1"
25.4.4.1+1-graalce/release:JAVA_VERSION="25.0.4.1.1"
25.4.4.1+1-graalce/release:GRAALVM_VERSION="25.4.4.1.1"
On 25.0.2 the two keys agree. From 25.1 on, the train takes the middle slot and the JDK's update digits follow: 25.4.4.1.1 is major 25, train 4, JDK update 4.1.1, and the CE release titles pair 25.1.3 with JDK 25.0.3 the same way. SDKMAN throws in a third spelling for free: Oracle GraalVM 25.3.4.1 installs as 25.3.4+1.r25-graal.
The image tag drops the GraalVM version entirely. 25i4 is "25 Innovation 4": the CE binary release is titled GraalVM Community 25 Innovation 4 (graal 25.4.4.1.1, jdk 25.0.4.1.1). Everything after the dash is the JDK, which makes the tag look like a 25.0-line version. On the old builder it would have been one.
The string 25.0.4.1 alone names at least five things: an OpenJDK update, an Oracle GraalVM security patch on the 25.0 line (18 August), Mandrel 25.0.4.1-Final (28 August), Liberica NIK 25.0.4.1, and the JDK under GraalVM 25.3.4.1.
CE's free 25.0 line stopped shipping binaries in January#
The upgrade on the to-do list had a real reason behind it, just not the one it named. The CE release list ends its 25.0 line at jdk-25.0.2 on 20 January. The next CE binary is 25.1.3, on 30 June. The calendar's CE column has a dash where 25.0.3 (April), 25.0.4 (July) and 25.0.5 (October) would be. Oracle GraalVM got all three.
From 25.1 on, both editions ship a feature release a month, and the calendar is blunt about the previous ones: "The most recent feature release supersedes all previous feature releases."
The floating image tag shows what that means for anyone who pinned the major version. On 25 October I asked ghcr for each tag's digest, one HEAD per tag on /v2/graalvm/graalvm-community/manifests/<tag> with an anonymous pull token:
$ ./ghcr-digests.sh 25 25.0.2 25i4 25i4-25.0.4.1.1
25 sha256:7eeb80438dcda5edfcc58e804ce919018d2bf40ef61ddbb555936a8ba2a216aa
25.0.2 sha256:7eeb80438dcda5edfcc58e804ce919018d2bf40ef61ddbb555936a8ba2a216aa
25i4 sha256:a7b4810d7c755e9627feaa1459eb5a93338643b16d745d4f3fc86db71e5da7f5
25i4-25.0.4.1.1 sha256:a7b4810d7c755e9627feaa1459eb5a93338643b16d745d4f3fc86db71e5da7f5
:25 is the 25.0.2 image, the same digest my September builds pinned. 25 follows the 25.0 line, and the 25.0 line stopped shipping binaries. Its source didn't: the graalvm-community-jdk25u tags run to vm-25.0.5, and Mandrel builds from them.
Following the CE trains means a new one about once a month (the next one, 25.5.5.0.3, slipped to 3 November), and each train can move defaults. The benchmark took that trade on 16 October and met two new defaults in the one jump: a reflection default that turned a missing metric into a hung /prometheus, and PIE as the default link mode, which Quarkus overrides on its own.
What CE 25.4 already ships#
The to-do list's upgrade was supposed to bring the 25.1+ defaults. All of them shipped in 25.4.4.1.1, the builder I already had:
- 25.1: Serial
Adaptive2, mark-compact in the old generation. "On average, this reduces memory usage and often improves throughput and latency." - 25.2: compressed 32-bit references by default, which caps the heap at 32 GB.
- 25.3: priority inlining as the default inliner, fast paths for
synchronized. - 25.4: optional identity hash code fields with Serial GC, and outlined
StringBuilderappends for smaller binaries.
None of these notes carries an edition restriction, and none comes with a measurement. "On average" is as specific as they get.
What Oracle GraalVM adds, lever by lever#
The docs mark these Native Image features as missing from CE:
| Lever | The docs | What it moves |
|---|---|---|
PGO: --pgo-instrument, a representative run, then --pgo | "PGO is not available in GraalVM Community Edition." | CPU per request, size |
GraalNN static profiler at -O3 | "-O3 and -O2 are identical in GraalVM Community Edition." | CPU per request, no profiling run |
| G1 for native images | Linux AMD64 and AArch64, "(Not available in GraalVM Community Edition.)" | pause times |
| Embedded SBOM | on by default, "(Not available in GraalVM Community Edition.)" | supply chain |
The only numbers are Oracle's. The JDK 24 notes credit GraalNN with "a 7.9% peak performance improvement observed across a wide range of microservices benchmarks". The PGO walkthrough calls its own demo app "not-illustrative-of-the-real-world".
Here is why I care. On my benchmark's imperative app, with virtual threads on both sides and no memory limit, the CE native builds spent 1.06× to 1.42× the JVM's CPU per request, depending on the framework. What's left on the to-do list, in order, is inference and untested: Oracle GraalVM with PGO and the Serial GC, then GraalNN -O3, then -march=native. G1 isn't on the list. It is a pause-time lever, and the one capped measurement below has it spending more CPU, not less.
The 6× G1 memory gap is an uncapped heap#
The G1 figure reached my notes as 3.68 GB against Serial's 611 MB, with a conclusion attached: G1 is the wrong fit at 128–256 MB. It comes from the footprint guide's StringManipulation stress test, run on "a machine with 48GB of memory, 8 CPUs". These are the six comparable runs the guide prints, peak RSS as /usr/bin/time reports it. A seventh, Epsilon, ran 6.4× the iterations into an OutOfMemoryError, and the guide calls it not comparable.
| Run | elapsed | user+sys | maxresident (KiB) | MiB |
|---|---|---|---|---|
| HotSpot, G1 | 3.35 s | 7.18 s | 4046128 | 3951.3 |
| Native, Serial, no cap | 10.10 s | 10.06 s | 611272 | 596.9 |
| Native, G1, no cap | 3.85 s | 6.24 s | 3707920 | 3621.0 |
Native, Serial, -Xmx512m | 10.99 s | 10.93 s | 590404 | 576.6 |
Native, G1, -Xmx512m | 5.13 s | 15.40 s | 554004 | 541.0 |
Native, G1, -R:MaxHeapSize=512m | 5.33 s | 15.31 s | 552292 | 539.3 |
Uncapped, G1's ceiling on that host was 25 % of physical memory, 12 GB, and it grew to 3.5 GiB inside it. Serial's ceiling was higher still, 80 %, and Serial stayed under 600 MiB. That is the 6.07× gap. Give both collectors the same 512 MiB cap and G1 lands at 0.94× Serial's RSS. It finishes in under half the wall-clock time and burns 15.40 s of CPU to Serial's 10.93 s, 41 % more. Small detail: the guide's prose calls the uncapped G1 run "3.68GB", but its own printed line says 3707920 KiB, 3.54 GiB. The table uses the printed lines.
I found no published measurement of G1 for native images at 128–256 MB, Oracle's included. My guess, untested, is that a small container with a CPU quota makes G1's trade worse, since it bought its speed at 512 MiB with 300 % CPU.
Oracle GraalVM's GFTC licence, read precisely#
CE is GPLv2 with the Classpath Exception. Oracle GraalVM ships under the "GraalVM Free Terms and Conditions (GFTC) including License for Early Adopter Versions", last updated 12 June 2023. The licence text allows "internal business operations" and redistribution as long as "You do not charge Your licensees any fees". Native executables come up by name: "those portions of a Program included in" software produced by running it, "such as may be produced by use of GraalVM Native Image", count as an unmodified Program. So the GraalVM code inside your binary travels under the same no-fee redistribution terms. Hosted use comes from the FAQ, which calls it "generally considered" internal use; the licence text never mentions hosting.
The terms have moved before. The downloads page says CPU releases of GraalVM for JDK 17.0.13 and later, and Oracle GraalVM for JDK 21.0.13, are "released under the GraalVM OTN license". Nothing says the same will happen to 25, and nothing says it won't, so it pays to record which licence each build came under.
What Oracle discontinued in September 2025#
Oracle's post "Detaching GraalVM from the Java Ecosystem Train" (15 September 2025) is easy to read as Oracle killing Native Image. The live page returns 403 to me; the Wayback copy reads:
GraalVM for JDK 24 was the final GraalVM release licensed and supported as part of Oracle Java SE Products.
GraalVM Early Adopter technology, including Native Image, is being discontinued for Java SE Product customers.
The scope sits at the end of that second sentence, and Oracle now pursues startup and footprint in OpenJDK's Project Leyden. Native Image itself kept shipping: 25.1 through 25.4 came out one a month from June, in both editions. Leyden's AOT cache is already a tier in this benchmark, where a broken cache keeps serving 200s by default.
Which GraalVM for a pinned :25 and a small container#
Three ways forward, each with a cost this post has already priced:
- Follow the CE trains. Free, patched, 25.1+ defaults included; re-read the build log's
vendor version:line and rerun the numbers at each one. - Oracle GraalVM under GFTC, 25.0.x or the trains. It brings PGO and GraalNN, the CPU levers CE lacks, at the price of a profiling run for PGO and a licence that has moved under 17 and 21 before.
- A CE-derived 25.0 line. Mandrel 25.0.4.1 is "a downstream distribution of the GraalVM community edition" (CE
vm-25.0.4.1) and Quarkus's default builder image. Liberica NIK is "Based on GraalVM Open Source", ships musl builds, and its releases list 25.0.3, 25.0.4 and 25.0.4.1. Both are CE-based, so neither should offer PGO or G1, and as 25.0 builds neither should carry the 25.1+ defaults; I haven't read a build log from either.
Staying put is not a fourth option. As of 26 October, the calendar's planned rows run to February 2027, and both 25.0 updates on them, 25.0.5.1 and 25.0.6, have a dash in the CE column. Nothing on that calendar moves :25.
Filed under: java, graalvm, native-image, docker, licensing
Read next#
- Health 200, API 200, Prometheus 000 — what the jump from 25.0.2 to 25i4 broke on the same image.
- Same Builder, Different Binary — the same builder banner on 18 binaries, and a 25.1 default that split them.
- A Broken AOT Cache Still Serves 200 — Leyden, where Oracle now pursues startup and footprint, as a benchmark tier.
- Native Image vs JVM at 128 MB — the CE-only Serial result, and the "What About Oracle GraalVM?" section this post picks up.
Sources#
- GraalVM release calendar — numbering rule, monthly trains since 25.1, the CE dashes for 25.0.3–25.0.5 and for the planned 25.0.5.1 and 25.0.6, 25.5.5.0.3 planned for 3 November 2026 (checked 26 October).
- GraalVM release notes 25.1, 25.2, 25.3, 25.4 — the defaults quoted above; 25.4.4.1.1 based on OpenJDK 25.0.4.1.
- GraalVM for JDK 24 release notes — GraalNN, 7.9 %, Oracle GraalVM only.
- Oracle GraalVM 25 release notes — the Oracle-only 25.0 updates, 25.0.5 on 20 October 2026.
- graalvm-ce-builds releases — last 25.0-line CE binary
jdk-25.0.2; "25 Innovation" titles for 25.1.3–25.4.4.1.1. - graalvm-community-jdk25u tags — CE 25.0 source tags up to
vm-25.0.5. - GraalVM container images — tag format; the
25i*and25.0.*tags were read from the ghcr registry API on 24 and 25 October 2026. - Native Image optimizations, PGO, PGO basic usage —
-O3=-O2in CE, PGO and GraalNN not in CE, the demo disclaimer. - Native Image memory management — G1 not in CE; default heap 80 % (Serial) and 25 % (G1) of physical memory.
- Optimize memory footprint guide — the six comparable
StringManipulationruns, plus an Epsilon run the guide calls not comparable. - SBOM support — embedded by default, not in CE.
- GraalVM Free Terms and Conditions — the GFTC text.
- GraalVM FAQ — CE licence, hosted use, Mandrel and Liberica NIK as CE downstreams.
- GraalVM downloads — the GraalVM OTN licence for 17.0.13+ and 21.0.13 CPUs.
- Detaching GraalVM from the Java Ecosystem Train (Oracle, 15 September 2025; 403 to automated fetches) — read via the Wayback raw copy.
- Oracle Java SE Support Roadmap (where docs.oracle.com/en/graalvm/support-roadmap.html redirects) — GraalVM for JDK 24 the final version in Java SE Products.
- Mandrel 25.0.4.1-Final — release text: CE downstream from
vm-25.0.4.1, no polyglot. - Liberica Native Image Kit — "Based on GraalVM Open Source", musl builds; BellSoft NIK release API and LibericaNIK releases — NIK 25.0.3, 25.0.4, 25.0.4.1.