leyden: starting fast
AOT caches today, AOT code next.
- chapter
- chapter 18.02
- reading time
- 8 min read
- preview
Project Leyden makes java start and warm up faster by shifting work in time: things every run used to redo (loading and linking classes, profiling, now compiling) happen once, in a training run, and get saved in an AOT cache that later runs map in.
as of october 2026, the cache holds loaded and linked classes (java 24), method profiles (25) and heap objects for any GC (26). compiled code (JEP 544) is targeted to jdk 28. the long-term idea, condensers, is still a design note.
the picture
you know how a JVM gets faster the longer it runs, and the first minute is the slow part? that's the JVM rediscovering, every single time, what your app needs: which classes, linked how, which methods are hot, and what to compile them into. the AOT and Leyden chapter shows how to use what exists today, with measurements. this one is about where it's headed.
think of a theatre. a touring show that builds its sets from planks every night before the curtain goes up spends most of the evening building. Leyden is a dress rehearsal (the training run) where the crew builds everything once and loads it onto the truck (the cache). every show after that just wheels the sets out. each java release lets the crew pre-build one more kind of thing.
a spectrum, not a switch
java's startup story used to be two extremes: the fully dynamic JVM (slow to start, fast at peak, everything works) and a native image (instant start, a closed world). Leyden's bet is that you shouldn't have to pick an end. you should be able to move along the line, shifting more work earlier and accepting only the constraints you actually need.
moved earliercompilation: C1 and C2 code for hot methods, compiled during training and stored in the cache, so they start out compiled. JEP 544 reports 65 to 80% faster startup on five framework benchmarks, against 50 to 70% for the cache alone.
you give upthe code only loads on the same CPU features and the same GC as the training run; otherwise it's skipped (the classes and profiles still work). and a bigger cache.
notice what every stop up to AOT code has in common: you give up almost nothing. reflection, dynamic class loading, agents, all of it still works, because the cache is a head start, not a contract. if the cache doesn't fit the run (wrong JDK, different classpath, a class that changed), the JVM just does the work itself. that's the difference from a native image, and it's why Leyden can ship in pieces.
what jdk 28 adds
if JEP 544 lands as targeted, the commands don't change at all. same training run, same flag in production, and the cache now also carries machine code:
# jdk 28 (JEP 544, targeted). illustrative: the same two commands as on jdk 25
java -XX:AOTCacheOutput=app.aot -jar app.jar # training run, then the cache is assembled
java -XX:AOTCache=app.aot -jar app.jar # production: hot methods start out compiledhover a stage.
JEP 544's own measurements (jdk 28 early access, a two-core linux x64 machine, so treat them as the authors' numbers, not yours):
| benchmark | AOT cache alone | cache + AOT code |
|---|---|---|
| startup, five framework benchmarks | about 50 to 70% faster | about 65 to 80% faster |
| warmup, javac, first of 20 iterations | about 30% faster | about 75% faster, near steady state by the 4th |
AOT code doesn't replace the JIT. it's a starting point: if a method later turns out to need different optimizations (the code was compiled for what training saw), it's deoptimized and the JIT takes over as usual. and the code is only used where it fits: the JEP supports aarch64 and x64 at first, needs the same CPU features as training, and needs the same garbage collector, because compiled code has the collector's barriers baked in.
condensers: the long game
the leyden project's design note "Toward Condensers" (2023) describes where this goes. a condenser is a transformation you run on your application ahead of time. it can shift computation earlier (precompute, preinitialize, compile), and it can do more if you let it constrain the program: promise that no new classes appear at run time, say, and a condenser can specialize much harder. condensers compose, and the output of each is still a normal java application, so you can stop at whatever point on the spectrum you're comfortable with.
the AOT cache is the first practical piece of that idea (its "transformation" is a training run). as of october 2026 there's no condenser JEP and no API, just the design and the premain branch where prototypes happen.
compiled C1 and C2 code for hot methods goes into the cache too. same commands as on 25. aarch64 and x64 only at first, and the code is GC-specific.
- leyden = shift work from every run to a training run, and save it in an AOT cache.
- shipped: loaded + linked classes (24), one-step training and profiles (25), objects for any GC (26).
- targeted for jdk 28: compiled code in the cache (JEP 544). same commands, needs the same CPU and GC.
- every piece is a head start, not a contract: if it doesn't match, the JVM does the work itself.
- the endgame is condensers: a spectrum of opt-in constraints. as of october 2026, still a design note.