What is Spring Native? | Game Changer

Reading Time: 11 minutes

What is Spring Native?

Struggling in reducing the size of your Spring applications fat deployment artifacts? Its become a real problem in this Cloud era, especially when considering a microservices and/or serverless architecture. Learn how Spring Native hits a home run and puts Spring back in the game.

Announcing Spring Native | Beta Release

Spring Native is capable of dramatically shrinking the size of those fat Spring jars into standalone executables, know as native images. All of this is performed via the GraalVM native-image compiler which performs a whole set of optimizations at build time through static analysis. More on this later.

With Spring Native, we get millisecond startup times, improved performance and reduce our memory footprint substantially. This is already the case in Beta, so expect even more improvements to come.

The Problems Spring Native Solves

 

Spring applications have slowly bloated over the years and have unfortunately not been the best fit for a microservices and/or serverless architecture. They tend to be too big and slow to spin up in containerized environments, especially when compared to other languages.

Spring application are too bloated but no one want to admit it

It’s the elephant in the room that we all don’t want to take about. We love Java/Spring and have invested so much into it but the truth is that many companies/developers have looked at other options because of this.

Spring Native is coming to the rescue and just hit a home run with the beta release. The grand slam is coming!

The Spring Native project has been (and still is) a huge undertaking.  The reason for this has to do with how different Spring and GraalVM behave. Especially in regards to build time versus runtime behaviors. Spring performs runtime decisions which are very dynamic in nature like creating proxies, lazy-loading etc ..

On the other hand GraalVM needs everything ahead of time (AOT) … at build time with the classpath set in stone before loading the application.  So how did the GraalVM and Spring team bridge the gap?

Spring developed a Spring AOT (Ahead Of Time) parser to create configuration information files which the GraalVM compiler needs to perform the conversion to a binary. The Spring AOT parser is made available to us via either a maven or gradle plugin which I will show you later on (maven only).

This is a huge deal when we start deploying microservices and/or serverless architectures. We need fast startup times and low memory footprints. This will increase the application density on a host, improve performace, reduce costs for on-demand resources used and even be able to fit under the serverless quota limits that some Cloud providers impose on things like maximum memory consumption or payload size.

So basically, Spring Native now puts us back in the game in terms of being able to compete with other technologies, especially for deployment architectures involving containerization.

Spring Native Example | Test Drive

You can start experimenting with Spring Native as of now via start.spring.io with Spring Boot version 2.4.4 on your own as Spring Native is already available for selection in the dependencies list.

Spring Native on spring io

 

Let’s take a test drive and git clone the Spring GraalVM native samples from GitHub ..

Once in the samples directory, import the petclinic-jdbc project into your IDE.

The first thing you have to look at is the pom.xml file for those new experimental dependencies and maven plugins. I have only included what is relevant to Spring Native.

You can see that we start off with a different parent pom dependency which is native based – experimental. A noteworthy build plugin is the Spring AOT parser I mentioned earlier. This plugin is critical in creating those configuration files needed by GraalVM in order to perform the compilation to a native image.

What is not so obvious is the experimental tomcat server which is there. It has been optimized for a Spring Native deployment. It will shave off 3.5M of memory from the final container image. These things add up!

 

Open up a terminal window in your IDE to build and run the native application packaged in a lightweight container image.

You’ll  notice that Spring-native builds takes much longer than your Spring-JVM based builds. Again, this is due to the GraalVM optimizations which don’t come for free. You pay a price at build time so you have to be patient 🙁

At the end of the build process, the Spring-native image gets copied into a Docker Image – performed by either Maven or Gradle plugin. Spring uses buildpacks within that plugin which is an alternative way to using a Dockerfile and is very compact/optimized.

On top of that, the docker image doesn’t include the JVM since the native image doesn’t need it – resulting in a very lightweight container at runtime. The build creates the following docker image “petclinic-jdbc:0.0.1-SNAPSHOT”.

My Spring-native build lasted 6:37 min .. ouch!

Spring Native Long Build Times | What to do?

This does raise an important point though. These long build times mean that it’s not a good fit for fast developer cycles like … code, compile,test as in your IDE. No one wants to wait several minutes extra on each development cycle. Having said that, what do we do?

In development, you’ll still want to continue to use a Spring-JVM setup in order to maintain those fast development cycles, so do not change that … it’s business as usual. However, what will change is how the application gets treated in your CI/CD pipelines. We’re able to absorb the higher build times in CI/CD environments in exchange for lighting fast deployments and lower resource usage. A great fit!

Spring Native | Run Docker Image

At the end of the build, you’ll need to list you docker images so you need to have docker – install docker. If your not too clear on how Java/Spring applications get containerized then check out my post How to Create Docker Image for Java Application

This is what I see …

We can see that the image is 142MB. Let’s run the Pet Clinic Spring native application with the following docker command.

Here is my console output. Look at the startup time on line 32 … 0.139 seconds which is 139 milliseconds … wow, that’s incredible!

We can see the resource footprint using the “docker stats” command which shows 57.17 MB of memory with 0.04% CPU ..

You can also follow along by watching the YouTube tutorial below

Comparing Spring-Native vs Spring-JVM | Pet Clinic

So how do these numbers compare to the Spring JVM based pet clinic application that you would normally run? I will clone the following git project and perform a full build into a container image as done before.

The above builds, runs about 40 tests and packages the jar file in a container image which takes  01:03 min as seen above. If you instead ran a “mvn package” then it would take less time, realize that there is overhead in building the container image. Obviously, the Spring-JVM based project has a big advantage at build time over the Spring-native version.

The next question is, how fast does it take the Spring-JVM pet clinic application to run? We can see in the console output above that the spring-boot maven plugin creates the “spring-petclinic:2.4.2 docker image. Let’s run it.

Running the JVM based application, took 7.898 seconds to deploy. This is much slower than the Spring-native based application, but we expected this.

Spring JVM vs Spring Native | Summary of Results

 

We can see the following Spring-Native and Spring-JVM based container images listed out below via “docker images”. There is a big difference in their image size. Spring-Native clearly takes up less disk space (only 143MB instead of 261MB) which will translate to smaller runtime containers.

When both containers are run side by side, we can see via “docker stats” that Spring-Native is a again a clear leader in resource consumption. The memory usage is practically an order of magnitude less (57.17 MiB instead of 508.8MiB). The cpu utilization is also reduced but not enough in this sample to make a big deal about it.

 

Spring Native Drawbacks and Trade-offs

It is not all roses! At this point, we can see that all this comes at the cost of much longer build times however, I already mentioned how to handle this previously.

Static analysis of the entire application is performed at build time in order to gather configuration information on things like reflection, resources, and dynamic proxies. This means that there are times that you have to provide additional configuration in the form of “hints” in your code (like an annotation or property) in order to help GraalVM port code from the JVM to binary.

Honestly, it’s a little messy now and improvements will be made in this area by the Spring team. Here’s a simple example which highlights the fact that GraalVM only supports JDK proxies and not CGLIB proxies hence the need for  ..

Of course, I would just create a new annotation called @SpringBootNativeApplication in order to abstract any other properties we might want to set. I don’t want to show case examples of these “hints” becacuse I expect this to change quite a bit in the future.

At this time (beta release), not all Spring services are candidates for Spring Native which means that not all Spring Boot starters are supported right now. That will obviously change but for now, here is the list of Spring Boot started that are supported.

 

Spring Native | Summary

 

Spring Native is only in Beta right now but it already posts very impressive numbers. Looks like we will eventually have to choose between a Spring JVM and Spring Native application in the Spring’s next major release.

Spring Native is an experimental feature for now but it’s here to stay and will improve by leaps and bounds. Remember how Docker just started out as a developer tool for Ubuntu? You can’t go anywhere today without talking about containers. It’s going to be the same with Spring Native.

Make no mistake, this is a game changer. Through Spring Native, Spring  has a real chance in becoming a prime candidate for small, quick and optimized running applications in this ever demanding containerized world.