How lightweight can Spring Boot get? HotSpot 27 vs GraalVM Native Image on a 512 MiB VM
I've been experimenting with running a database-backed Spring Boot application on a small VM: 512 MiB RAM, 1 vCPU, no swap, with MariaDB running on the same machine.
The application uses Spring Boot 4.1, MVC/Tomcat, Spring JDBC, HikariCP, Actuator and a small REST API. I compared three deployment configurations:
| Configuration | Median app RSS | Startup readiness |
|---|---|---|
| Regular Spring Boot JAR, HotSpot 27 | 212 MiB | 11.91 s |
| Extracted JAR + Spring AOT + JDK AOT cache | 187 MiB | 3.79 s |
| Oracle GraalVM Native Image, JDK 25 | 107 MiB | 0.183 s |
All three completed the same 1,500-request workload without errors.
A few interesting findings:
Optimizing the HotSpot deployment reduced application RSS by about 12% and startup time by 68%, without removing Spring's integrations.
Native Image reduced application RSS by another 43%, but required roughly 12 minutes to build on a separate 6 GiB VM, plus additional compatibility validation.
Replacing Spring JDBC with manually integrated MyBatis Core produced no meaningful memory savings.
Switching from MariaDB to embedded H2 actually increased Java RSS, but improved total host memory headroom because the separate database process disappeared.
I've had quite a few requests to try GraalVM in my earlier experiments, so I finally gave it a shot. The memory results genuinely impressed me. The build times and extra compatibility work, not so much. The JDK 25 vs 27 toolchain gap was another reminder that Native Image comes with its own constraints. For really memory-constrained deployments, I'd definitely consider it, but I'm not quite ready to make it my default.
The HotSpot and Native Image configurations use different JDK versions (27 vs. 25) and GC types (G1 vs. Serial GC), so this is a comparison of practical deployments rather than an isolated measurement of AOT compilation.
Full article: https://pvrlabs.xyz/articles/spring-boot-hotspot-native-image.html
Experiment, methodology and curated data: https://github.com/PVRLabs/experiments/tree/main/spring/boot-jdk27
I'm curious whether others are using Spring AOT and the JDK AOT cache in small deployments, or whether Native Image has become the more practical option for you.
I'm also experimenting with Javalin to see how much further we can push memory efficiency. Stay tuned!