As the developer of 10MinuteMail, a disposable email service, I have been running the service on Java for many years. I am always looking at new technologies and embracing new things. Recently I decided to not only upgrade from SpringBoot 2.x to 3.x, but also to migrate the application from Java to GraalVM Native.
What is GraalVM Native?
GraalVM is a universal virtual machine for running applications written in JavaScript, Python, Ruby, and R, as well as JVM-based languages like Java, Scala, Kotlin, and Groovy. GraalVM native is a feature of GraalVM that allows for ahead-of-time (AOT) compilation of JVM-based applications to native machine code, resulting in faster startup times and lower memory usage.
SpringBoot 3 has incorporated the Spring Native project into the main Spring Boot stream, allowing for easy development of Native compatible Spring applications.
Why migrate to GraalVM Native?
I found that by migrating to GraalVM Native, I was able to see significant improvements in the application’s performance. The startup time of the application was reduced from approximately 30 seconds down to about 3 ms, and more importantly the memory usage was also significantly reduced from 6.6 GB down to 1 GB, with the same throughput and CPU utilization. This allows me to reduce my hardware footprint and costs, while providing the same level of service to the website users.
The migration process was not without its challenges.
There were a few hurdles in the migration. Partly because it was really three upgrades done at once: Migration from SpringBoot 2.x to 3.x, from Java 11 to Java 19, and from the JVM to a Native binary. There were some API changes that had to accommodated. I use a front end templating system called Thymeleaf, and also use a 3rd party Layout Dialect for Thymeleaf. Unfortunately the Layout Dialect does not work as a native executable, due to its use of dynamic Groovy. Thankfully I was able to find a full Java port of the library which solved that issue.
I also had to change how I was handling configuration a bit. Previously I relied on Spring Profiles to manage the different configurations needed for my Local, Development, and Production environments. With a GraalVM Native binary you cannot use Profiles that way, so I had to extract my environment specific configurations to local files and reference that location on startup.
One of the other drawbacks, is while you can build and test quickly by running the application in a normal JVM during development, building the native binary file is much slower, taking approximately 10 minutes as a GitHub Action (see my post about Building Spring Boot 3 GraalVM Native Images with GitHub Actions), or 2 minutes and 20 seconds on my laptop.
I’ve also lost the ability to use the New Relic Java APM Agent to monitor and troubleshoot performance and errors within the application.
These are all things to keep in mind. However the massive improvements to boot time and memory usage make it well worth the trade offs. This type of technology will allow for more Java based Serverless micro-services, and quicker auto-scaling actions.
Conclusion
Migrating 10MinuteMail from Java to GraalVM Native was a big step for me, but it was a necessary one in order to ensure that the application could continue to meet the demands of its growing user base. The improvements in performance and scalability that I have seen since the migration have more than justified the effort that was put into the migration process. If you are experiencing performance issues with your Java-based application, especially in the context of Serverless architectures, GraalVM Native may be worth considering as a solution.
Leave a Reply