DEV Community

Cover image for Let's make SpringBoot app start faster
Mitz
Mitz

Posted on

Let's make SpringBoot app start faster

"How Fast is Spring?"

is a session at Spring One Platform 2018. I watched the video and tried it by myself. So I introduce here what I did and the results.

I recommend you to watch this session video if you haven't yet. It's so interesting.

https://springoneplatform.io/2018/sessions/how-fast-is-spring-

Today's source code

https://github.com/bufferings/spring-boot-startup-mybench

↓I used OpenJDK 11.

❯ java --version openjdk 11.0.1 2018-10-16 OpenJDK Runtime Environment 18.9 (build 11.0.1+13) OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode) 
Enter fullscreen mode Exit fullscreen mode

↓You can run all the benchmarks like this. It will take a while because it runs all the benchmarks.

❯ ./mvnw clean package ❯ (cd benchmarks/; java -jar target/benchmarks.jar) 
Enter fullscreen mode Exit fullscreen mode

1. FluxBaseline

↓I created a project using SpringInitializr only with Reactive Web. Then, I wrote a tiny controller with WebMVC style.

@SpringBootApplication @RestController public class DemoApplication { @GetMapping("/") public String home() { return "Hello"; } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } 
Enter fullscreen mode Exit fullscreen mode

↓The version of Spring Boot was 2.1.0.RELEASE.

 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> 
Enter fullscreen mode Exit fullscreen mode

↓It took 2.938 ± 0.287 s/op to start.

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op 
Enter fullscreen mode Exit fullscreen mode

Now I got a baseline to check the startup time. Let's start from here.

2. WebMVC

↓I wondered what about WebMVC, not WebFlux? So I tried it. Maybe does it just mean the comparison of Tomcat and Netty?

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case02_Web ss 10 3.281 ± 0.342 s/op 
Enter fullscreen mode Exit fullscreen mode

WebFlux is a bit faster, isn't it?

3. spring-context-indexer

Next, I tried spring-context-indexer which seems to create component index.

 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-indexer</artifactId> <optional>true</optional> </dependency> 
Enter fullscreen mode Exit fullscreen mode

↓Um... a little slower?

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case03_WithContextIndexer ss 10 3.063 ± 0.102 s/op 
Enter fullscreen mode Exit fullscreen mode

↓I checked spring.components, and found it contained only 1 component. I see... I should try with a bigger project to know the effect.

# #Sun Nov 04 18:42:59 JST 2018 com.example.DemoApplication=org.springframework.stereotype.Component 
Enter fullscreen mode Exit fullscreen mode

4. Lazy Initialization

Tried Lazy Init.

@Configuration public class LazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { for (String beanName : beanFactory.getBeanDefinitionNames()) { beanFactory.getBeanDefinition(beanName).setLazyInit(true); } } } 
Enter fullscreen mode Exit fullscreen mode

↓Here's the result. It became just a little bit faster.

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case04_WithLazyInit ss 10 2.844 ± 0.129 s/op 
Enter fullscreen mode Exit fullscreen mode

5. NoVerify

Ran with -noverify:

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case05_WithNoVerifyOption ss 10 2.582 ± 0.060 s/op 
Enter fullscreen mode Exit fullscreen mode

It became a little faster. I don't know what it means, so I need to check it later sometime.

6. TieredStopAtLevel

Ran with -XX:TieredStopAtLevel=1:

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case06_WithTieredStopAtLevel1Option ss 10 1.980 ± 0.037 s/op 
Enter fullscreen mode Exit fullscreen mode

Uh, much faster! It took less than 2 seconds. But I don't know this flag, too. So I will check it later.

7. Specify SpringConfigLocation explicitly

Ran with -Dspring.config.location=classpath:/application.properties:

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case07_WithSpringConfigLocationOption ss 10 3.026 ± 0.139 s/op 
Enter fullscreen mode Exit fullscreen mode

Um, it became slower.

8. Turn off JMX

Ran with -Dspring.jmx.enabled=false:

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case08_WithJmxDisabledOption ss 10 2.877 ± 0.097 s/op 
Enter fullscreen mode Exit fullscreen mode

It became a little bit faster.

9. Exclude Logback

From here, I try to exclude libraries. At first, excluding Logback:

 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-logging</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> </dependency> 
Enter fullscreen mode Exit fullscreen mode

Here's the result:

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case09_WithoutLogback ss 10 2.904 ± 0.096 s/op 
Enter fullscreen mode Exit fullscreen mode

mm... slightly improved?

10. Exclude Jackson

Next is Jackson

 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-json</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> 
Enter fullscreen mode Exit fullscreen mode

The result:

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case10_WithoutJackson ss 10 2.789 ± 0.093 s/op 
Enter fullscreen mode Exit fullscreen mode

It became a little bit faster.

11. Exclude HibernateValidator

 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> <exclusions> <exclusion> <artifactId>hibernate-validator</artifactId> <groupId>org.hibernate.validator</groupId> </exclusion> </exclusions> </dependency> 
Enter fullscreen mode Exit fullscreen mode

Here's the result:

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case11_WithoutHibernateValidator ss 10 2.857 ± 0.084 s/op 
Enter fullscreen mode Exit fullscreen mode

Slightly improved, too.

This is the end of library exclusion.

12. AppCDS

AppCDS (Application Class Data Sharing) was included in Oracle JDK as a commercial feature. But it became available from OpenJDK 10.

It seems AppCDS dumps information into a shared archive, so startup time becomes shorter.

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case12_WithAppCds ss 10 2.957 ± 0.079 s/op 
Enter fullscreen mode Exit fullscreen mode

mm... it wasn't faster... then I checked articles about CDS, and found the reason.

With SpringBoot FatJAR, the libraries are out of the scope of CDS.

13. Flux with Thin Launcher

Uh, I'm sorry, but the benchmark name "Exploded" is wrong. Once I tried to explode the FatJAR, but I couldn't use CDS with the exploded JAR after all. So I switched to use Thin Launcher. Please take the benchmark name "Exploded" as "Thin Launcher".

Before using CDS, I would like to check the speed of JAR file packaged with Thin Launcher.

 <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot.experimental</groupId> <artifactId>spring-boot-thin-layout</artifactId> <version>1.0.15.RELEASE</version> </dependency> </dependencies> </plugin> </plugins> 
Enter fullscreen mode Exit fullscreen mode

Although I used Thin Launcher to package the app, I didn't use the launch class of Thin Launcher, but specified Main class to make the startup time as fast as possible.

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case13_Exploded ss 10 2.476 ± 0.091 s/op 
Enter fullscreen mode Exit fullscreen mode

hum, a bit faster, isn't it?

14. Thin Launcher + CDS

Now I would like to apply AppCDS to it.

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case14_ExplodedWithAppCds ss 10 1.535 ± 0.036 s/op 
Enter fullscreen mode Exit fullscreen mode

Wow! It became much faster!

15. All applied

Finally, I applied everything.

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case15_AllApplied ss 10 0.801 ± 0.037 s/op 
Enter fullscreen mode Exit fullscreen mode

Less than 1 second! (∩´∀`)∩yay

One more step

In the Dave's session, he mentioned "Functional Bean Definitions", tried improvements with Spring without SpringBoot and the app became much faster. I need to learn more to understand them.

Result list

Benchmark Mode Cnt Score Error Units MyBenchmark.case01_FluxBaseline ss 10 2.938 ± 0.287 s/op MyBenchmark.case02_Web ss 10 3.281 ± 0.342 s/op MyBenchmark.case03_WithContextIndexer ss 10 3.063 ± 0.102 s/op MyBenchmark.case04_WithLazyInit ss 10 2.844 ± 0.129 s/op MyBenchmark.case05_WithNoVerifyOption ss 10 2.582 ± 0.060 s/op MyBenchmark.case06_WithTieredStopAtLevel1Option ss 10 1.980 ± 0.037 s/op MyBenchmark.case07_WithSpringConfigLocationOption ss 10 3.026 ± 0.139 s/op MyBenchmark.case08_WithJmxDisabledOption ss 10 2.877 ± 0.097 s/op MyBenchmark.case09_WithoutLogback ss 10 2.904 ± 0.096 s/op MyBenchmark.case10_WithoutJackson ss 10 2.789 ± 0.093 s/op MyBenchmark.case11_WithoutHibernateValidator ss 10 2.857 ± 0.084 s/op MyBenchmark.case12_WithAppCds ss 10 2.957 ± 0.079 s/op MyBenchmark.case13_Exploded ss 10 2.476 ± 0.091 s/op MyBenchmark.case14_ExplodedWithAppCds ss 10 1.535 ± 0.036 s/op MyBenchmark.case15_AllApplied ss 10 0.801 ± 0.037 s/op 
Enter fullscreen mode Exit fullscreen mode

It was really interesting. Thank you!

Top comments (4)

Collapse
 
stealthmusic profile image
Jan Wedel

Nice comparison. Would be great to undestand what those flags actually do.
And what happens, you exclude Jackson? You just return a string but will Spring still be able serialize an object to json?
BTW: If it’s about startup time, you should try GraalVM 😎

Collapse
 
alainvanhout profile image
Alain Van Hout

A really interesting exposiion, thanks! Probably just the scientist in me, but plotting the summary in a graph would be really cool :).

(Just one comment: when two measurements have different values but widely overlap when you take into account the error flags, then the correct thing would be to say that there seems to be no difference between the two)

Collapse
 
kic profile image
KIC

you can also try to add gralvm and native image

Collapse
 
pmgysel profile image
Philipp Gysel

Interesting experiment. It‘s amazing how much room for startup optimization there is..