In the week of 22 October 2018, me and two colleagues visited Oracle CodeOne (previously known as JavaOne) in San Francisco. One of the talks was from Graeme Rocher, the creator of the Grails framework. His talk was about Micronaut. The new kid on the block on JVM-based frameworks.
Increasing memory consumption
Nowadays, there are a lot of organisations who choose Spring Boot as their preferred framework when developing new microservices. Mainly because Spring Boot is really easy to setup, there is a vibrant community and a lot of guides to get you up and running. A problem with Spring in general, is that the memory consumption grows quite steadily once you begin adding functionality to your microservice. When you have a complete landscape consisting of numerous Spring-based microservices, the operational costs can increase and the ease of development becomes less.
Runtime vs Compile-time
That is where Micronaut comes into play. It was created to eliminate aforementioned problems while still offering the ease of development and setting everything up. You probably wonder how they achieve that? Mainly it comes down to how they work internally. Spring does dependency injection based on reflection. It also saves and caches a lot of information during runtime which clogs up the memory once you start adding functionality.
Micronaut does this different. It does dependency injection based on annotation processors which precompile the metadata needed. Because everything needed for dependency injection is done at compile time, it doesn’t have to do this at runtime and that can save a lot of memory in the long run.
In order to show you the difference between both frameworks, I constructed a little example. The examples are ran on a MacBook Pro (15-inch, 2017).
At first I created a simple “/hello-world” endpoint in both the Micronaut and Spring application. Code for these examples can be found at https://github.com/jacksierkstra/hello-world-micronaut and https://github.com/jacksierkstra/hello-world-spring.
In order to build a runnable docker container, you’ll just have to clone the repository and run “docker build -t micronaut:latest .” In the cloned Micronaut repository. If you want to build the Spring Docker container, you’ll have to clone that repository and run “docker build -t spring:latest .” In the cloned Spring repository.
In order to run these containers side by side, you can issue these simple commands.
- docker run -d -p 8080:8080 spring:latest
- docker run -d -p 8081:8080 micronaut:latest
Now you have the two examples running and you can verify that by running “docker ps”. The output should be similar to the image below.
You can verify that the containers are running correctly by doing a http request to them. In the images below you can see that they both contain a “/hello-world” endpoint with a simple JSON response.
And if you check the output of the “docker stats” command, you’ll see that the Micronaut container consumes about half of the memory than the Spring container does.
Above example is very simple, but it shows that Micronaut is much more memory efficient. This is purely because it was designed to do this. If you add up a couple of Spring containers, you’ll notice that you are very quickly battling against your machine limits.