GraalVM is a sophisticated attempt to retrofit Java for the cloud-native era by trading build-time simplicity for runtime efficiency. While the performance gains are undeniable, the reliance on complex optimizations like PGO highlights the growing friction between Java’s dynamic roots and its static future.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
Spring Native: The Future of Fast and Efficient Spring Applications by Alina Yurenko @ Spring I/OAdded:
[music] >> Okay, hi everyone. I must say it's not an easy task to take over the stage after Victor, but I'm going to do my best.
So, my name is Alina. I'm happy to be back here after a break last year, and I'm here to tell you everything that I think will be the future of Spring Native applications. So, everything you need to know from the Spring side, from the GraalVM side. I will focus on some of the features that we released in the latest release in 25, and then I will talk about some of the forward-looking, more into the future, still kind of experimental projects. And most of the demos that I will show here today, it will be the first time that I show them aside from Pet Clinic. So, let's hope that the demo gods are on our side.
And the way this talk will be structured is that I will have a grand total of three slides, two out of them which are intro and outro. So, essentially there will be one slide, and then a lot of demos to show you all the things that I want to show. So, let's go.
So, for one, I want to talk about our GraalVM 25 release.
And I'm wondering how many people in the room have moved to Java 25 already.
Okay, so a vast majority I would say, which is a very good sign, but also I suppose if you are at a conference, then you are always up-to-date on the latest trends and you try to follow them in your work as well. So, this is also something that we do for GraalVM. If you have been at the keynote yesterday, at Jürgen's keynote, which I suppose most of the people were at, he mentioned that they are moving to Java 25 and GraalVM 25 as a baseline for Spring Framework 7.0, and we are adopting a similar approach for the GraalVM releases. So, at the moment we will stay focused on Java 25 as our baseline, and this will be the case for our stable releases, so the ones that just receive security updates, as well as feature releases. So, we will keep adding new GraalVM features to that baseline, but the Java version will stay stable for now. It will be the LTS, the Java 25 version. And of course, you might tell me that LTS is a vendor concept, it's not an official OpenJDK concept, each vendor decides for themselves, any Java release can be an LTS release if a vendor decides so. But in general, I think there is this sort of shared knowledge and understanding in the Java community that some releases are more long-lived than others, and Java 25 is such one release. So, just as Spring and many libraries and frameworks in the ecosystem, we will focus on 25.
Now, I also have some news for the languages ecosystem. Anyone in the room a Python fan maybe?
Okay, I'm at a Java and Spring conference, so I think I see a grand total of one hand, but moving forward we want to invest a bit more time and effort into our Python implementation. We already have a very good Python implementation. We can run many applications, including some that are using AI AI and ML packages, and you can embed them in your Java applications or just run Python programs in GraalVM, but we have very ambitious goals for our Python implementation, and moving forward I think throughout this year and next year we want to achieve even more and be very competitive in the Python space as a runtime to execute Python applications.
Similarly, funny enough, it's not on the slide, but we also have a JavaScript implementation, so it's quite mature, it's called GraalJS, and I think maybe one of the main sort of value proposition and use cases there is to extend Java applications with scripting capabilities. And we have, at least that I know of, one big sort of customer user using GraalJS in that regard, and that is NetSuite, who are using JavaScript from GraalVM to add scripting capabilities to their Java application.
And one more big ecosystem that is quite important for us is WebAssembly. So, I know that Sergi runs also a very popular WebAssembly conference here in Barcelona, so I assume some people in the room are also fans of WebAssembly or at least are trying to experiment with WebAssembly, and we also do a lot of work in that space that relates both to running WebAssembly on GraalVM as a runtime, but also executing, compiling Java JVM applications to WebAssembly, and then executing them in environments such as browser and Node. And we are in a very good touch with the WebAssembly community, they are very excited for what we built with GraalVM and WebAssembly, so more news coming there, and if we have time I will also show you specifically a WebAssembly demo that hopefully will be fun.
But this is not to say that we are abandoning Java community, not at all.
We love Java, we use Java heavily. Our baseline is almost primarily exclusively in Java. So, Java and Native Image remain our big focus. We have a lot of users in the Spring community, in other Java communities, so Native Image stays very important for us, and today I will show you some of the latest and newest Native Image features.
Lastly, I will just make a short teaser because this is not released yet, but we have a lot of work ongoing in the AI space. So, some trivial AI tasks we can do already, such as compile your application using some sort of LLM and just run it as Native Image. This is easy and this is done and this has been working out of the box, but we are working on something ambitious in the AI space. So, I think in the next weeks or at latest months you will hear about a new big release from us in that space, so stay tuned.
And with that, I would like to wrap up this sort of official part, and there are a lot of other big projects underway which I did not mention on this slide, but they are ongoing. And as an open source project, we have a community roadmap on GitHub. So, if you want to see what we've been up to or our team has been up to, you can consult that community roadmap and see what we are working on.
Okay, with that this slides part is over, and I would like to transition to demos.
So, I would like to talk a bit about performance and peak performance in Native Image and how you can achieve it.
I can actually use a very nice note that I've been set up for this demo during the lunch. Someone approached me and just said, "Hey Alina, why is no one talking about PGO conferences?" And I was like, "Well, that is not exactly true. Like I saw people talking about PGO at conferences, but that sets me up to talk with you about PGO and why or why not maybe people talk about it and why it is so important."
So, we've been doing this for a while, that's why I don't think I need to do a lot of intro to PGO, but just very quickly. So, if you have heard that maybe Native Image is not quite as performant as the JVM at runtime, there is a reason why people think so, and I'm actually wondering how many of you have heard such an opinion that Native Image is slower when it comes to peak throughput than the JVM.
Okay, some hands, but likely not too many.
So, the JVM, as we all know, is very powerful at peak throughput. It is specifically tailored for that specific goal, and there is a number of reasons and ways that it accomplishes this high throughput, but probably the most important is the dynamic profiling.
So, we know that the JVM, as it executes your code, it observes your application, it sees what's being loaded, what's being executed, and based on that it makes assumptions and then actually executes actions to optimize those specific parts of the application which are the most often executed, because it makes sense to spend those compiler efforts in those often executed parts, and this way you will get the most result for the time and resources that you have spent. And this happens at runtime when your application runs, and Native Image, clearly being a citizen of this IoT world, does not have such capacity out of the box.
So, the way we approach this is by using PGO, which is profile guided optimizations, where you can build you can do basically two builds. One will be sort of training run, or as we call it instrumented run, where you build one Native Image with a PGO instrument flag.
You receive this instrumented application, you run it, you collect profiles in a similar fashion how the JVM collects profiles, and then when you shut down the application, you receive this profiles file, and you can use it when you actually build the next Native Image for production, and this way you get sort of an insight into the future, so what will happen and which methods and loops will be most often executed.
Now, there is a conversation to be had about how exactly the JVM profiles your application, how much of it happens at startup, how much of it happens during runtime, and how many of those assumptions are being revisited down the line, but that's a topic for a different story. What I would like to talk here and show you is a one-step sort of PGO process, which you might have heard about as ML PGO, but the official name is called ML-enabled profile inference.
So, you would think that in the year of 2026, in the year of AI and ML and everything, it should be possible to just sort of look at your application and with a certain level of accuracy predict how it will behave at runtime and which part of that application will be most important.
We thought the same thing, and actually long before LLMs and AI became so widespread, and we have added an ML PGO optimization in Native Image, which No, the wrong chart. We need ML PGO, this one.
So, we have added capability in native image to include a pre-trained ML model, which again on a high level looks at your application and analyzes it and then tries to infer predict for each possible method uh what how likely is each executing execution path. So, let's say we see some sort of if statement, so how likely that it will be true or false that statement will be executed. And this ML model analyzes your application while we build it and it instructs the compiler uh with those profiles to inform the compiler to make better decisions.
And then as we build the application, we look at the whole application. So, we have the analysis phase, we have those inferred profiles from ML, and as I discovered when I was working on this presentation, which makes perfect sense, but I guess I didn't just think about it a lot, in Graal compiler itself, we have an internal cost model for an application. So, let's say if you are looking at an operation that is simple int addition, we estimate that it's going to take maybe one cycle of CPU, but then if it's something more expensive such as loading something from memory, we understand that it's going to be more expensive. So, we just have this sort of like cost model table inside the compiler itself. It's a bit of a side note, but I find it quite fascinating.
So, we can actually discover it for ourselves by looking at the compiler sources. So, this is the source code of the Graal compiler on GitHub. And we here we see that we have this integer div rem node. So, it's doing division and remainder operations, and we see that it's estimated here that this might take 16 CPU cycles.
So, having all that information about your application, what we see during the analysis, what the ML profiles tell us, and what is the estimated cost of doing each of those operations, we actually optimize your app.
And it maybe it sounds a little bit confusing, but you can understand that it's even more complicated in practice, but as a user, it's not that hard. So, let's see it in action.
For one, let's just run our old friend PetClinic on the JVM as a baseline.
Okay, it started and it took it 4 seconds to start.
Uh just as a side note, this runs on my Linux instance, which is not super beefy, but it's just a nice clean isolated environment that it's not being messed up by me screen sharing or like running slides or anything else.
So, it should be running. Uh let's just remap the ports, so we have a fresh forward to my local machine.
And let's see.
Okay, so it runs very standard application. I'm sure you have seen it already like 10 times at this conference, nothing exciting to see there. But let's see it running as a native image application.
So, I have already compiled a few versions of this app, many actually.
They are all sitting here in target directory with different names, and I want to start by benchmarking the very first simple version of this application, and we will execute a script that is bench native.
So, this is the content of the script.
We will start the app, we will let it sleep for 1 second because it's native image doesn't need a lot of time to start.
And then we will send a little bit of warming up load, and then we will send the actual uh benchmarking load. So, we will do two rounds of measurements, but we will only pay attention to those after being warmed up. Even as a native application doesn't need that much warming up, but still it's a fair thing to do.
Uh so, let's do it. Bench native only not optimized. I just want a simple one.
So, it's going to warm up for 10 seconds, and then it will bench it for 20 more seconds. Now, what's interesting while it's being benchmarked, we are not just starting the app and bombing one endpoint with requests.
Even though I have limited time for this talk, I tried to be a bit more smart about it. So, in fact, the load that we are sending is defined in this Lua script, where we are accessing different endpoints. So, the ones for owner, the ones for vets, the ones for visits, and we try to access different endpoints in both read and write data. So, hopefully it's a bit more representative of what a normal person could have done on the website, so a bit more fair as a benchmark.
Now, this is done. The numbers look good to me, and this plain native image just compiled clone from GitHub and compiled as is is at 757 requests per second. So, this is our baseline, and as a baseline when you don't specify any compiler optimizations, we compile at the {dash}O2 compilation level, which is sort of a middle ground between no optimizations and max optimizations. So, {dash}O2 is advanced optimizations, but at a reasonable time. It's sort of a trade-off between build time and peak throughput.
Now, let's see if we can make it a bit faster, and I have another version of this application that uses the {dash}O3 compiler uh optimization level. So, it's using {dash}O3, but I specifically turned off this ML profile inference because I want to hopefully show the impact that the ML inference can do with the application performance. So, this tab is all compiler optimizations in place, but no ML. So, just compiler doing as best as it can sort of in the dark without the profiling information.
Okay, uh the numbers might be coming in soon, but we can already sort of see that it gets kind of faster with {dash}O2, right? So, enabling more optimizations makes the application faster.
Now, uh only not here, I want to open it here.
Let's use the {dash}O3 level and ML. So, this is just using the {dash}O3 level, which under the hood includes ML. So, let's try and see if the application can be faster if we add some ML to it. Now, let me hide it. Who thinks this last round with ML will be faster?
Okay, I don't think you are very convinced about the power of ML or I don't know.
Let's see. I hope that it's going to be faster.
Okay, this round is completed. Now, sometimes I see a bit more delta in those changes. This is a microbenchmark, so it's not very representative, but I think we can agree that going from O2 to O3 compiler level and adding ML does improve our performance of our application.
Now, for all fairness and to address that person's comment, why is no one showing PGO at conferences, let me try and show you one last build of this application or one last benchmark. So, this is using not ML inferred profiles, but the actual profiles that I have collected by doing those two runs, where I have built an instrumented build, collected profiles, and then triggered the next build with the actual profiles in place.
So, this one is using uh my actual collected profiles, but also it's using Z One GC. So, I expect it to be significantly faster than previous runs.
Okay, this one is done. It is somewhat faster. I cannot say that it's groundbreaking faster. As you can notice here, there is still some variance to the benchmark. It's in general, you know, not very representative to run something for 40 seconds, but I don't want to dedicate the whole session to it.
Uh the the LDR that I want you to take from this four rounds of benchmarking is that using the {dash}O3 compilation level, the one that we used here, gives you a very good peak throughput along with a very simple build. So, the concern that some people have around PGO is that it is requires those two steps, right? An instrumented run and then the production build, and not everyone is willing or not everyone cares about peak performance quite as much to do those two steps in deployment. Now, if you don't want to do it, if you just want to have one simple, I don't know, button in your CI/CD to build and deploy an artifact, I would quite recommend to check out at least this {dash}O3 compilation level with ML PGO. That's the one that gives you this peak throughput. I think most of us can be happy with 163 reqs per second. But then if you have an application that is really critical in terms of peak throughput, so maybe you have some high load environment, maybe you have very strict requirements for response times to users, I do recommend investing into two-step process PGO build, where you collect actual real profiles. They are unbeatable.
Now, there is a lot of numbers on the screen. I can understand that it might be a little bit overwhelming, but one more thing that I want to add here is in this last round with user collected profiles, but also maximum optimizations, I have also measured the RSS of that specific process, and we can see that peak RSS that consumed was consumed by the process as we were running the benchmark is roughly slightly about 200 megs.
And this is not an idle application.
This is an application actually running and taking significant workload.
So, native image is often considered for things like startup or packaging size, but what I think is not always so understood or emphasized is the memory usage, and memory we know is quite expensive these days. So, if you would like to reduce your memory usage, I would argue that native image is a very good solution for that while still giving you this good peak throughput.
So, two conclusions from this slide. If you want a simple deployment and ML powered peak performance, I would suggest that you go with the {dash} O3 performance optimization level. And if you want to have the absolute maximum native image can offer you, please go ahead with user provided PGO.
Uh now, let's go back to our agenda. So, we talked about ML PGO. Now, I would like to derail for maybe 10 minutes into a more sort of enterprisey practical, maybe slightly a bit more boring, but necessary part of the native image world and talk about things like security, migration, et cetera.
Uh so, one thing is that we have introduced this interesting what we call zero configuration migration flag to native image called H preserve. And this is meant to solve one particular problem. I'm wondering if it happened to you, maybe not now, but in the past that you build a native image and then you start it and then it complains about something like class X that was supposed to be available at runtime hasn't been found during the runtime. So, does anyone ever see that sort of message?
Okay, quite a few hands.
So, this flag, what it allows you to do, and I think it will be specifically useful for more sort of legacy application or maybe if you have some sort of libraries and dependencies that don't have the necessarily config for native image, is this allows you to include certain parts of your application completely into that native image build.
So, as you know, by default, native image is quite sort of strong on removing everything that you don't need and we have many reasons for it. We want to give you a small image, and we want it to be performant, we want it to be secure, so we just strip away everything that you don't need, but it can come at the cost that if you have an library that is not configured and instruct native image how to build correctly, it can break with this sort of error.
Now, to prevent that or at least try to prevent that, we are introducing this new flag where you can instruct us to include a whole package or module or put like provide a wildcard for us to understand which parts of your application need to be included completely. And this might solve at least a class of such problems. Now, I'm not going to say that they're going to it's going to solve all of those problems, but at least some of them will be solved and some of them come from the analysis phase where one class triggers initialization of a different class and a different class. This might be harder to tackle, but if just something that the class was not found because the library did not instruct us that a class needs to be available, then this flag will be quite useful there.
And sometimes people refer to this flag by calling it preserve equals all, but all will compile your whole application.
So, let's say I have path cleaning that has many dependencies. So, if I pass preserve all, it will include everything into that native image build. So, I would quite argue to be more granular and more precise with this flag. If you can narrow down the issue to a specific, let's say package, it is much more preferable because you don't need to pull everything from your project into this one native executable.
Now, continuing with this sort of enterprisey practical topic, I want to talk about security a little bit and I'm wondering how many of you are using S-bomb, so software bill of material for your applications.
Okay, quite a few people. I think more than when I usually ask this question.
So, in native image, as you can see, we have a million modes for how exactly you can produce and have your S-bomb available for your native image. The simplest probably way is to embed it inside your native executable, but also there are some other modes that are useful for third-party integrations. So, for example, for Spring Actuator that I will show in a second, you need to pass the option that is enable S-bomb equals classpath, and then S-bomb can understand then Spring Boot can understand that this is S-bomb from native image and expose it correctly under an Actuator endpoint.
We even have something quite crazy in native image, which is called class level S-bomb. This is rather new and I'm not sure how many people already use it in practice, but it's quite fascinating because as we build native image, we have a very good understanding of your project and what is inside it. So, on a class level and like per field level, we know what is in your application and what is actually needed in runtime. So, if you want to have a really detailed understanding of your application, you can produce a class level S-bomb and then you will receive this gigantic JSON file that instructs on a class level what is inside your native image.
Uh this can be quite interesting, especially for vulnerability scanning.
If you can boil down that I'm using maybe a dependency that is considered vulnerable, but those specific vulnerable parts of it I don't pull into my application. So, this way you can reduce false positives because even though the dependency is vulnerable, you are not using that part of it.
Uh but this is in a more experimental area, this class level S-bomb. I would like to show you the more practical as uh S-bomb sort of aspect of working with it. So, let's go here.
And here I have a compiled application only not on this one, but demo S-bomb.
So, this application has been built with S-bomb. You can see that it starts incredibly fast, not because of S-bomb, but it's because it's a very small application. And then if I go here and I do actuator, so Spring understands that this is a native image application that has built with S-bomb support and it just correctly discloses at the S-bomb Actuator endpoint that native image S-bomb. And this can be quite useful when let's say you work maybe in a corporate environment and there is some sort of security team that wants to scan your deployments and make sure that you are not vulnerable, that you are using everything that is secure and compliant.
So, you can just point them to an endpoint like this, maybe not to the outside world, but to those internal teams, and they can see exactly what are you using.
Now, what I quite like here is specifically for native image that we include the dependencies that you are using, but also we include what they depend on. And there are better ways in the Java world to analyze your application and dependencies and what is depending on what, but this is very specific to native image. So, here I can see that in my final produced native image, I have for example Spring Web, which is sort of expected, but then Spring Web in return pulls in things like auto configure, Web MVC, et cetera.
So, it depends on those things. And this is very specific to native image, so only to the dependencies and files that have ended up in native image.
So, this I think is pretty cool and can be quite useful in production environments, but also if you don't want to maybe expose this endpoint, if you don't think it's a good practice, you can also build a native image that embeds an S-bomb and then you can scan it and um scan it for vulnerabilities.
So, I'm going to here extract an S-bomb that is in that native image. There is an easier way to do it, but for the sake of demo, I just like extracting from the actual compiled native image. And I'm going to immediately pipe it into Drive and it's going to tell me if my application is secure.
Okay, I must say I did patch it up yesterday because yesterday when I was running the demo, it turned out that I had some outdated dependencies, but today, as you can see, we are all fine. We are running with the latest uh vulnerability DB. We correctly detected 46 packages in my application, but no vulnerabilities were found. So, we are all good.
Now, in a similar spirit of sort of security and maybe I don't know, privacy of your applications, we have added this advanced obfuscation in GraalVM 25. Now, why is it advanced? We don't call it advanced to make it sound like an appealing marketing term. There is actually a very good reason why it is advanced, and that is because native image itself is considered by many people as an obfuscation tool. So, if you take your application from the JVM bytecode that is an input to native image and you build a native image out of it, you have a native executable, so an optimized machine code, and for that optimized machine code, it is quite hard to go back to the initial sources of your application. I'm not going to say that it's impossible, it's not, but it is quite hard compared to bytecode to just sort of decipher your application and go back to the source code. So, this is already a feature of native image as the way it is built, but in this release, we have also added this advanced obfuscation, so you can build with this flag.
And let's say you are I don't know, working for a company that has some sort of very precious IP. So, you have some your unique proprietary knowledge that you want to secure from the world and make sure it doesn't leak anywhere.
Maybe you're working in FinTech and you have some super secret trading algorithm that you always sell stocks that go below 50 days average or something, and you think this is your unique knowledge, you don't want it to reveal to the world. So, you can build your application with this advanced obfuscation flag, and what it will do for your sort of user code, it will replace those symbols that you normally see in the code when let's say an exception is thrown and a stack trace is revealed. So, those will be replaced with generic symbols such as A, B, C, D, et cetera. So, let's say if your application crashes for your clients, instead of saying seeing that stack trace that reveals your secret class, secret algorithm, et cetera, they will just see those generic code symbols. So, that is a way to protect your IP if you have some unique knowledge.
Now, the last part of this sort of more enterprise-y practical things is I want to uh use I guess as most people use in this conference use a little bit of Spring AI and also continue with the pets topic and I have built a little um pet store application that is using semantic search to retrieve items from a database.
Uh so, let's see it in action.
Uh what is that one?
Pet vector search is the one.
So, we will look at the sources in a second. First, I want to show you that I have my native image. Although, this one is a bit more substantial than some of the demos that I have showed before and please in terms of the size, but let's see what it does.
So, it starts and it connects hopefully to a cloud database.
Okay, it did.
And you can see that the startup here is a bit longer, but this is largely for startup of native image itself. It just needs as a part of that starting life cycle to connect to a database so it can work with data. But overall, you can tell me what you think. I think that this below 1 second to start and connect to a database and be ready to work with data. I think this is a very reasonable time.
Now, application itself it's not anything pretty. It is quite a bare-bones sort of REST API application that I have built to show this one specific capability of Spring AI and GraalVM, but basically it just has my main class data loader that loads some pet store items into that database and then we have a search which combines two things. So, our search uses Spring AI for semantic search. So, we have all our application pet store data embedded in the database and between them we can search for relevant items using semantic search. But also we are filtering using some business logic because I wanted to avoid some fall false positive and I wanted to eliminate some of that typical for LLM non-determinism. So, I didn't want let LLM decide what is and isn't the right uh response. So, primarily we are using uh Spring AI for semantic search among embeddings and then we have business logic to filter the correct results.
Uh so, let's see the application is running and it's just an endpoint. Maybe I could have built some pretty UI for it with Vaadin, but honestly I could not have bothered. So, we're just going to uh send requests to the endpoint.
So, let's imagine we are not looking at my terminal, but we are looking at something pretty like Amazon or whatever is the store of your choice and we want to find let's say some cat food.
Okay, let's see. We have received some results and they are all I think relevant to our user query. Uh one thing that I would like to point out here that some are just precise matches. So, probably could have been done without AI, but then some of them are actually matching the semantic meaning. So, here for example, we can match that this chicken soup is actually a food item and similarly for this tuna souffle we can again match it even though it does not specifically say that it's a food item, we are able to retrieve it.
And we can proceed with more queries. We can maybe look for some dog toys and this will retrieve us again items that look to me like they could be dog items.
And then just one uh last query see what it does. So, if we look for fish tanks, it gave me two tanks. No, one tank and then two accessory items for tanks which I think are a reasonable result.
So, this is using Spring AI. This is using GraalVM running as native image and this is using Oracle database. And if you want to try it out, I think it's a really cool and easy way to build those sort of semantic search apps. I will share all the demos and links in the end.
Now, with that I see that I have 12 minutes left I guess including 5 minutes for Q&A. So, I would like to derail into a more sort of fun forward-looking whimsical uh demos and topics.
Okay.
The next thing I want to show for you is a Spring Shell application with a twist.
So, I think last time when I was here I might have shown you a Spring Shell application compiled as a native image and of course as a CLI you want your CLI apps to be fast and very responsive. So, native image is a perfect fit.
This year I'm back with this demo, but as the title says with a twist. So, let's see what the twist is.
The application is here and by looking at the source, I think we can agree that this looks remotely like a normal Spring Shell application, right? We see the normal Spring Shell uh classes and annotations and we have one method that I have implemented on top of that base and that is cute LS which is a more cute version of the Linux LS utility that gives us a nicer list of current files in the directory.
So, can we agree that this looks like a normal Spring Shell apps?
I think we can.
So, let's see if we can run it and what if anything makes it so special.
So, native Spring, but not boot shell.
And I have a little uh run script here.
This one I think. Yes.
Okay, it starts. It's a normal Spring output command. We know that it comes with a very nice help that lets us see what is inside. So, there are built-in commands and then there are the commands that I have built which is cute LS.
And we can retrieve things like version.
It all works and we can call my method.
So, LS and it even takes arguments. So, we can do like LS target.
Okay.
So, I think it works. It looks fine.
What makes it weird though? If we look inside that run script, this is what we run.
What do you think this is?
Guys, you can shout. It's a big room, but maybe I will hear.
Sorry?
Okay, I cannot hear unfortunately all the way from here, but basically what we have and the way this application runs is here.
So, those two files are actually running our Spring application. Now, if you look here, I think it becomes now apparent that this is web assembly. So, I took our Spring Shell application and I compiled it to web assembly with something called web image.
Uh so, web image is a different way of building applications with GraalVM.
Normally, when you think GraalVM and native image, you think that it takes JVM bytecode and produces native executables and that is true, but that's just the main use case. Technically, for GraalVM, there is very little limit to what it can take as an input and produce as an output. So, in this case, I have built the application and we have successfully run it with our node by passing some flags for the specific version. So, this is Spring Shell or Spring Boot in general running as a web assembly code.
Now, if you are curious the query to look for it and find more details about it, this is called web image. I'm not going to say that you can take tomorrow any random application out there and compile it to web assembly, but this is something we are working on actually again together with the web assembly community and over the time I expect more and more features to arrive and similarly the web assembly community is very excited about the work that we are doing.
So, this was Spring running as web assembly.
Now, another thing that I quite like and let's see how many demos I can show is all those new CLI and rich terminal applications. Many people say that 2026 will be the year of CLI renaissance and terminal applications renaissance and I agree with them. So, one thing that I would like to show for you is the application built by Dan Vega. I'm sure if you are at a Spring conference, you all know Dan. He's a great part of the Spring community and a great educator.
Follow him on YouTube if you haven't yet, but I want to show a demo that he has built. So, let's see Spring I know we need to go up.
Uh Spring Initializer something.
Okay, this one.
So, I can again run it. It's running as Spring native application. It loads and this you can infer from the name is a Spring Initializer running here conveniently locally on my local machine and I can use it just as a normal Spring Initializer by configuring my application. So, I think here I'm quite happy with everything. Config properties amazing. Now, here obviously I want to choose native image. Maybe not Lombok, but probably Spring uh yeah, that is fine and let's maybe yeah, let's choose this one. Okay, and it comes with a nice help. So, you can see that there is a lot of navigation.
You can do a lot of things here, but I think I'm quite happy with this application. So, I can just click generate and it generates and then I can open it in the whatever environment I prefer and it's a fully blown Spring project.
Now, it's uh it's something quite fun and I absolutely love those sort of applications and just generating Spring applications like that, I think it is very cool.
>> [snorts] >> And it was built with one cool framework that I will show you in a second, but before I do, I want to show another thing that I have built. So, one day browsing Reddit, I saw I saw randomly this project. Where is it?
Uh Okay, this one.
Uh so, I have heard that there are a lot of people from Germany and other Swiss neighbors at the conference, so you might know what SBB is. SBB is our local railway company in Switzerland, and this person, I don't know them from Reddit, they have built a terminal application for SBB, but they built it in Go, and I'm not the biggest fan of Go in the world, so I thought maybe we can recreate something similar in Java and obviously in GraalVM. So, I did, and let's see how it runs.
SBB terminal.
Now, I don't think you can appreciate enough the design. It might not look like a lot to you, but I did a few iterations here to make it look pretty.
Now, what makes it so special? It is actually a fully functional SBB, at least like timetable and route planning application. So, here I can choose where I want to travel from. Let's say it's Zurich HB uh Zurich HB, and then to where to maybe Geneva, and then uh yeah, I think this is fine.
Time to now. Okay, I think this is good.
And it loads for me all the current connections, and I can choose how I want to travel. And not only it loads those connections, but it's actually real data that it gets from the API. And if I check now in my application, it's going to tell me that the data is real.
Now, what makes it so cool is that it's also built using this Tambo UI library or framework, and I want to quickly show it to you because I think it is very cool. So, this helps you build those sort of rich UI terminal applications easily in Java. So, if you wanted ever to build your own cloud code or something like that, this is the way to do it. And it is built by great community of people in the Java ecosystem. So, if you look here, you will recognize some people like Cedric, Max, Andres, uh Guillaume. So, it's If you always wanted to have your cool app, sort of like cloud code or something like that, that is the way to do it.
Now, I would like to switch to the future of GraalVM. Okay, I have 5 minutes, so I'm going to really need to speed up. Sorry, guys. So, this is going to go fast. The future of GraalVM. One thing is we are receiving a contribution for Shenandoah in native image. You have always asked us to have more uh GC options in native image, and one such option that is being now added by the community is Shenandoah. There is a contribution from the Amazon engineering team, and we work with them to make it work in native image. So, likely this was already merged and supported in GraalJIT, and now native image is the next step. We have done the work from our side to enable more GCs in native image, and now this is coming up next.
So, I briefly mentioned web image, and uh you can read more about it. That's basically the JVM to web assembly compilation technology in GraalVM and native image.
And I would like to show very quickly those two very cool, I think, demos.
So, one thing will be uh GraalVM native image layers. I just need my chart. So, layers will be a brand new way to build Spring applications or applications in native image, where you can build your application in sort of two steps, where one, you define your base layer, that is your JDK, Spring dependencies, et cetera, and then your application is a thin layer on top. And then the base layer is used as sort of like Docker layers or shared library, and it is shared across instances of the application. So, let's say I can have five different applications using the same base, if that is, let's say, JDK and Spring base. And then compilation times can go much significantly lower, but also we can have even better resources shared in the cloud because the base is shared across all those instances, even if those are different apps.
This is super cool, and I never saw it never showed it in action, but now I think I'm ready. Oh, where is my connection broken? What is happening?
Good. Okay, let's check it out. Spring native layers. Now, I'm just going to run it, and as it runs, I will tell you what it does.
So, I have 10 applications compiled using layers, and they use the same base, and the base in this case is JDK, Spring, and all my Spring dependencies.
And we are running those 10 applications, and we are measuring memory as 10 applications run. And what makes it so cool here is that we measure RSS and PSS, where RSS is the memory occupied by the process, more or less, and then PSS is memory that is occupied by the sort of private memory of the process, and a fair chunk of that shared base that they are using. So, let's say if my base occupies 100 megs of RAM, and there are 10 applications using such a base, then each of them gets allocated 10 megs of that memory usage. So, we can see here that PSS is much lower because my applications are using the same base, so they can all benefit from that memory sharing. This is extremely cool, and we have already demos for it. I want you to try it out. Even though it is early days, I'm very excited about layers.
And one more cool demo, and the last demo for today will be Project Krama, and this might be my favorite GraalVM feature lately, if not ever.
And then Project Krama is an open world for native image, so it will allow loading new classes into your compiled native image. So, you can have your native executable compiled and built as always, but it can then at runtime load and execute new classes that I haven't seen before. This is extremely cool. I'm very happy about this work that our team did, and I would like to take part of the credit for this beautiful ticket name, which I came up with. I think it explains Krama very well. Now, let me see if I can show you quickly them the Krama in practice.
Okay, Krama demos.
And uh I have an application here that is built with Krama. So, this is not Krama itself, but it is built with Krama, so with this ability to load new classes. And for the sake of saving time, I will just explain that this application is compiled source launcher from the JDK, so one specific class from the JDK that loads uh that lets you execute Java source files. So, when you do Java something something.java, it in memory compiles your file and executes as a source code.
So, let's take it for a spin. Uh this And yeah, this looks completely diabolical, I'm aware. In case you're curious, this is being passed not for Krama, but for javac, because I need to invoke javac, and javac needs to know about my Java home. So, this Java home equals Java home, this is not Krama, this is javac. And if I do hello Krama, it prints for me hello Krama and retrieves the VM name, which is SubstrateVM in this case. Where is my hello Krama? So, this is the application that is being run, but I can pass to the exact same binary a different file, and it will be able to execute it. So, we can do even some cool terminal graphics.
So, this is again the same application, but it runs a totally different thing because it executes a different thing at runtime. Okay, and we can just do something like Krama and pass to it uh grabbit grabbit.java, and we see those messages. So, again, the same application, but it can load and execute different classes at runtime. And for Krama, there is still a lot of work to be done, but we are actively working on it. At the moment, we have already added a JIT compiler to Krama, so not only it's going to do things, but it's going to do things fast.
Now, with that, I think I'm just 1 minute over time, so I would like to wrap up by showing the necessary QR codes uh slide. So, one is with all the demos, notes, link, everything that I have used in today's presentation, and the one on the right is for questions.
If you want to ask me anything on social. And if you don't mind, I will take a very quick selfie with all of you.
Thank you so much for staying until now, and thank you. Please enjoy the conference. Thank you.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Introduction to Problem Solving Part - 1 | Lecture 1 | Intermediate DSA
ascensionix
107 views•2026-05-29
🚀 BCS613C Compiler Design | Module 1 to 5 Schema Evaluation 🔥 | VTU 6th Sem 💯 #VTU #bcs613c #exam
Pranavaa-y4y
104 views•2026-06-02











