The Last Train from Metropolis: Whatever Happened to GraalVM - JVM Weekly vol. 185
Quite a lot has piled up around GraalVM.
Christian Humer piece on script sandboxing caught my eye, and as usual - once I started looking around at how much had changed around GraalVM in the meantime, a whole edition came out of it. There are several stories here, seemingly independent of each other, but when you lay them side by side, they form one fairly coherent picture. Not in the sense of “Oracle has a grand strategy” - it’s just that the last few months have gone in a very specific direction, and it’s worth writing down before it disappears into the thicket of release notes.
We start with the biggest one, because it’s been a bit quiet about it.
1. Metropolis is officially behind us
If you weren’t following this, Project Metropolis was one of the most ambitious things ever started in OpenJDK. The goal: rewrite significant chunks of HotSpot from C++ into Java and replace C2 with Graal as the native JIT, compiled by Substrate VM. Java-on-Java. A JVM bootstrapping itself from its own, statically verifiable code instead of decades of hand-maintained C++. The kind of thing you only propose when you truly believe in your own tools.
What happened? GraalVM happened. Just not inside OpenJDK, but next to it. It’s worth tracing three stations along the same route, because this is not a story where Graal simply packed its bags and left.
Station one, JDK 17 (2021). JEP 410 pushes the experimental Java-based AOT and JIT out of OpenJDK. The justification: “we’ve seen little use of these features, and the maintenance cost is significant”. Out of the tree went jdk.aot (jaotc) and jdk.internal.vm.compiler (Graal itself). JVMCI stayed, so that an external party could plug in their own compiler from the side. Oracle murmured reassuringly that “developers who wish to use Graal can use GraalVM” - today that sentence sounds different than it did back then.
Station two, JavaOne 2022. Oracle tries to rebuild the bridge. The keynote announces that key pieces of GraalVM are coming back to OpenJDK, and in December Project Galahad launches for exactly that purpose. The plan was concrete: the Graal Compiler as an alternative to C2, then Native Image, then integration with Leyden, and future GraalVM CE releases based on the Galahad sources. It sounded like reconciliation after JEP 410, and for three years it looked that way - shared repositories with GraalVM, an updated galahad branch, and Metropolis still had a sensible future as the place where the final Java-on-Java integration would happen.
Station three, September 2025. Detaching GraalVM from the Java Ecosystem Train. Oracle makes a U-turn: GraalVM stays a separate product, on its own terms, with its own cycle, and is not coming back to OpenJDK. Galahad loses its raison d’être overnight (why keep a separate project for an integration we’re not doing?), and Metropolis - whose entire point was that Graal would one day return to HotSpot - is left as the last man defending the position.
The formal closure came in two acts. Galahad was dissolved in March 2026 with one of the most ironic justifications I’ve seen in OpenJDK - in short: “the project has become redundant in light of the September decision to detach GraalVM from the Java ecosystem”. Metropolis followed a month later - I wrote about it in vol. 173, but back then I didn’t yet have the full context of everything else that has come out of it over the past few weeks. Today it’s a bit clearer.
For JVM veterans this is an “end of an era”. There will be no self-hosting JIT in the mainline. Java-on-Java in the form that Rose and Reinhold used to talk about at JVMLS is officially on the shelf. GraalVM obviously still exists and is doing well - it’s still a great runtime, it still sits behind Native Image, and it’s still where most of the real innovation around Graal happens. But the ambition of putting all of that back into the JDK has just been officially folded up.
And that explains a lot of what happens next. Because if you’re not going back to the JDK, you have to build your own product with its own rhythms. Which is exactly what Oracle just did.
2. Its own calendar, the end of tracking the JDK
In May, Shaun Smith announced that GraalVM is moving to monthly feature releases. The 25.0 line stays as an LTS with quarterly CPUs, but new things ride on a separate track - no backports, “upgrade forward” and that’s it. 25.1 in June, 25.2 at the end of July, 25.3 in August. The calendar is published a year ahead.
But the meatier bit sits in the FAQ: there will be no GraalVM for JDK 26, 27, or 28. The next stop is Java 29. The justification is brutally simple: the library ecosystem doesn’t live on anything other than the LTS releases anyway, so building artifacts every six months for short-term JDKs was producing souvenirs for nobody. In other words: GraalVM has stopped being the JDK’s shadow, got its own clock, and quietly communicated along the way that Java’s cadence doesn’t matter much to it outside of the LTS releases. Someone on LinkedIn joked that “it’s like a partner in a relationship who stopped syncing with your calendar and started living their own life” - it kind of is.
The same consolidation covers one more quieter change: as of Polyglot 25.1, the optimizing Truffle runtime only works on GraalVM. The jargraal variant on plain OpenJDK, previously bolted on via --upgrade-module-path, is no longer supported. For years Truffle sold itself on the slogan “it also runs on your regular JDK”, and that slogan has quietly been taken off the shop window. All roads lead to Oracle’s platform - and it’s the same line of thinking as Metropolis: we’re not going back to the JDK, we’re building our own thing, in-house, on our own terms.
3. The sandbox lands in the free edition
Back to the article that provoked all of this. In his tutorial, Humer describes how to safely run code in GraalVM that your team didn’t write: customer scripts, plugins, and recently (and this is the framing you can’t miss) scripts generated by models. The running example is a discount rule in JS. Four policies: TRUSTED (shared heap with the host), CONSTRAINED (you only see what the host explicitly exported), ISOLATED (separate runtime, separate heap, mandatory CPU and memory limits), UNTRUSTED (the full browser threat model with constant blinding and entry point randomization to break JIT spraying).
The tutorial itself is solid, but the most interesting detail sits in the release notes, not in the tutorial. In 25.1, the ISOLATED and UNTRUSTED policies and hard resource limits landed in the Community Edition (for now for JS and WebAssembly; a sandbox for pure Python is in the works). Until recently, isolates and hard limits were a selling point of the paid Oracle GraalVM - today everyone has them. And Alina Yurenko 🇺🇦 , in the 25.1 announcement, says outright why: applications are looking for a safe way to run code written by models.
Before we go further, it’s worth setting the wider frame right away, because this is not a local GraalVM move. Over the last dozen or so months, the entire industry has been learning the hard way that if a model generates code, someone has to run that code safely - preferably with hard limits, an isolated runtime, and concrete boundaries on what it’s allowed to touch. Hence Code Interpreter at OpenAI and the whole “run this code in a sandbox and give me back the answer” approach, hence Anthropic with Computer Use, where the agent clicks around a desktop inside a container, hence MCP, where every server exposing tools to an LLM is a potential hole to close. Hence also the whole wave of infrastructure startups - E2B, Modal, Daytona - selling “a runtime for agents” as SaaS, priced like a regular API. Cloudflare Workers for AI, Vercel with the AI SDK, LangChain with its sandbox tooling, every no-code platform integrating models - everyone suddenly needs the same thing: a paranoid execution environment you can trust an agent with.
And now recall that GraalVM has had all of this - isolated runtimes, hard CPU and memory limits, a sandbox-safe JIT with constant blinding - for years. Until recently it was used by three big projects: NetSuite SuiteScript, Oracle Database MLE, and Picnic (38 million business rules a day). A solid niche, but a niche. Now the description “a paranoid runtime for JavaScript and WebAssembly, embeddable in a JVM backend” fits a product urgently needed by every team integrating an LLM with anything. And in that context, the decision to give it away for free in CE is unforced only on the surface: a ten-year investment in a niche suddenly turned out to be infrastructure for the entire AI wave, so it now needs to be seeded into the ecosystem quickly, before someone else (E2B on the JVM? Cloudflare with workerd?) fills that gap. It’s one of Oracle’s more economically interesting moves in recent years, and it fits exactly into the same calculations that every AI infrastructure vendor is making right now.
4. The closed world opens up a little
The sandbox, though, is only half of the pivot. The other half sits in Native Image and at first glance looks like heresy: -H:+RuntimeClassLoading (Crema) gives you a bytecode interpreter inside a native binary, capable of loading classes the builder has never heard of. -H:+GraalJITCompileAtRuntime (Ristretto) adds a JIT on top of that. Terminus makes the builder itself compile down to a native executable and host a guest JVM inside it by splitting the host and guest heaps.
Add it up: an interpreter, a JIT, dynamic class loading. A project that spent a decade carrying the virtual machine out of the binary is carefully carrying it back in - smaller, optional, on its own terms. The motivation is very practical, not ideological: Byte Buddy generates bytecode on the fly, javac annotation processors are pulled in dynamically, so if you want Native Image and those things at the same time, someone has to let that dynamism in. They let it in.
More interesting than the projects themselves is what all three are built on. The JDWP debugger in a native image? The core of Espresso. Crema? Espresso. Terminus? An Espresso context with a separate heap. Even the sandbox’s isolate architecture - separate heap, separate GC, separate JIT for the guest - is the same pattern. The meta-circular Java interpreter, for years a curiosity from the last slides of conference decks, is now carrying four different use cases at once. It has stopped being a research project; it’s infrastructure.
As an aside: this is a rather elegant closing of the Metropolis story. Java-on-Java didn’t come back to HotSpot, but it came back as the foundation of GraalVM. Through a different door, but it came back - only now it’s called Espresso and lives at Oracle Labs.
5. Leyden took the startup story
All of this sounds like a move made from a position of strength, but the pivot has a very concrete external motivation. Fast startup, for a decade the main selling point of Native Image, has finally got a real competitor inside the plain JVM.
Leyden matured in OpenJDK, and JEP 516 in JDK 26 finishes off the AOT cache, which previously didn’t work with ZGC. According to the team’s data, Spring PetClinic starts about 41% faster, with ~21 thousand classes loaded and linked from the get-go. The cost of entry: two flags and one training run. No auditing your dependency tree for reflection. Quarkus has already integrated it and honestly says how much the cache file weighs (39 MB for a minimal REST app, 198 MB for a big one)...
...but that’s a pain every ops person understands and knows how to budget for.
It’s still not native-binary territory, but it’s close enough to change the architectural calculus at a lot of companies. GraalVM, in response, is shifting its weight to where the competition still isn’t: size (images in 25.1 smaller by 3% on average, HelloWorld squeezed down to ~6.5 MB), footprint (the new Serial GC Adaptive2 policy cutting RSS, G1 finally on Macs with Apple Silicon), and - as in section 3 - isolation. Notice that the 25.1 announcement doesn’t have a single new slide about startup milliseconds. That’s not a coincidence.
6. Oracle disagrees with itself about AI
Since we’re on the subject of models - and this is the thread that surprised me the most last quarter - two flagship things under Oracle’s wing are now running completely different policies on AI-generated code. InfoQ covered it thoroughly:
The OpenJDK Governing Board, April 2026: no entry. Contributions must not contain content generated by LLMs, diffusion models, and “similar deep learning systems” - in code, text, images, PRs, emails, the wiki, JBS. The justification: (1) reviewers being flooded with PRs that look sensible but are incorrect, (2) unclear IP exposure, (3) the policy is “temporary”, we’ll revisit. Spell check, autocomplete, and IDE refactorings are allowed, as long as they are “not based on LLMs or similar”.
GraalVM (Oracle Labs, not governed by the OpenJDK GB), around the same time: a Coding Assistants Policy that explicitly allows using AI to prepare contributions, as long as the contributor takes responsibility. The wording is worth quoting: “Use of AI assistance does not create any presumption that a change is correct, review-ready, or exempt from normal scrutiny.” A reviewer can ask about provenance, design decisions, licensing, tests, or whether the author understands what they submitted. On June 3rd a Documentation Terminology and Style Guide for AI assistants was added on top - so that PRs from models look like PRs from the team. The GraalVM policy explicitly references the Linux Kernel’s model, not OpenJDK’s.
Same corporation. Same Oracle Contributor Agreement. Two different policies. OpenJDK says “we’re afraid of IP and a flood of reviews”, GraalVM says “the author’s accountability is enough for us”. For me, the most telling part is that GraalVM deliberately reached for the Linux model instead of its own intra-corporate OpenJDK policy. Someone here is picking a camp.
And this connects to another thing that’s easy to miss: the reachability metadata repository just crossed 1500 libraries, and the scale was delivered by a fleet of AI agents generating metadata from observed test executions, with a human as the final reviewer. To round it off, a Native Image skill for AI assistants landed in oracle/skills. GraalVM doesn’t just permit AI in contributions - it actively uses AI to solve its biggest historical problem, which was Native Image configuration.
7. It was always a project about polyglotism
Let’s pull it together. Metropolis is over, Graal hasn’t lived in OpenJDK for five years, GraalVM has its own calendar, the sandbox is in CE, Native Image is getting its interpreter back, Leyden ate the startup story, jargraal got dropped from the plain JDK, and Oracle disagrees with itself about AI. Quite a lot for a couple of weeks.
You could serve this as a list of release notes and individual political decisions, but all of it really follows from one fact that gets forgotten in everyday conversations about Java: GraalVM was never a “faster Java”. It started over a decade ago with the paper “One VM to Rule Them All” - a compiler written in Java, Truffle for building interpreters, a polyglot runtime on top of which sat JavaScript, Ruby, R, Python, LLVM, WebAssembly, and Java itself in the form of Espresso. Native Image was the loudest child of that family, loud enough that for a decade it drowned out the rest of its siblings. (A treat for collectors of full-circle moments: among the co-authors of that 2013 paper is a certain Christian Humer. Yes, the same one.)
The current shift toward sandboxing and executing other people’s code is not some new strategy. It’s a return to what GraalVM was doing from the start, dressed in a narrative that matches today’s problems (code from models, code from customers, dynamic code in containers). The way GraalVM describes itself today is closer to Würthinger’s original paper than to the marketing of 2019-2023: not “a faster Java”, but a place to run code that nobody on your team wrote, in several different languages along the way.
Metropolis was supposed to be the bridge to OpenJDK in that dream. Since that bridge doesn’t exist - and won’t - this entire world is closing up inside Oracle. That explains everything else: why jargraal got dropped, why there are no builds for the interim JDKs, why the roadmap looks like a product roadmap and not a feature roadmap.
You can like it or not, but at least it’s consistent.








