Web Architecture, Java Ecosystem, Software Craftsmanship

How to Increase your JVM Development Productivity

Posted on Oct 22, 2017

During development, it’s important to have fast feedback loops after code changes. Waiting for a cold restart of the application is not satisfying at all and slows down the development process. In this post, I show how we can reduce the turnaround time by using certain JVM arguments and class reloading. Besides those framework-independent means, I’ll give some tips for Vaadin and Spring Boot applications.

Increasing JVM Development Productivity

TL;DR

  • The JVM arguments -noverify -XX:TieredStopAtLevel=1 reduce the startup time by 30% - 45%!
  • However, it’s better to avoid a restart at all by utilizing hot code replacement
    • The normal hot code replacement of the JVM works out of the box. Just start the application in the debug mode and re-compile the modified Java file in the IDE. But the possible changes are limited to method bodies.
    • Spring-Loaded allows reloading files into a running JVM after more sophisticated changes (like removing or adding methods or fields).
  • The Spring Boot Developer Tools provide automatic restart and LiveReload support. But I still prefer the generic Spring-Loaded approach as it’s much faster and preserves the application state.
  • Vaadin: After the initial theme compilation, Vaadin’s on-the-fly compilation doesn’t add noticeable overhead. It safe to keep the production mode disabled during development.
Final run configurations in IntelliJ IDEA. They provide the means to influence the startup time and the class reloading capabilities.

Final run configurations in IntelliJ IDEA. They provide the means to influence the startup time and the class reloading capabilities.

Sources

I created a simple application with Vaadin and Spring Boot. I added some libraries in order to keep Spring Boot busy with bootstrapping during startup (Spring Security, JDBC auto configuration, h2 console, Actuator endpoints). This way, we can better see the impact of our optimizations. The sources can be found in my GitHub project development-productivity-vaadin-spring-boot. Try it out!

General Tipps

The following tips are framework-independent. They’ll work with every JVM application and IDE.

Reducing the Startup Time

During development, we usually restart our application multiple times. This takes time and delays our feedback cycles. Fortunately, there are the following VM arguments boosting our application startup speed:

  • -noverify or -Xverify:none: Disables the bytecode verification. So the classloader won’t check the bytecode for dangerous or disallowed behavior.
  • -XX:TieredStopAtLevel=1: Limits the optimizations of the HotSpot compiler and its runtime overhead.

Needless to say, but never use those VM arguments in production as they will reduce our application’s security and long-term performance.

I tried the VM arguments in my example application and wrote down the startup times printed by Spring Boot:

How the application has been started RUN DEBUG
Execute built jar with java -jar 9.6s
Execute main() in the IDE 10.1s 12.6s
Execute main() in the IDE with -noverify -XX:TieredStopAtLevel=1 5.7s 7.3s
Execute main() in the IDE with IDEA’s Run Configuration “Spring Boot” 5.7s 7.3s
  • So with -noverify and -XX:TieredStopAtLevel=1 we can significantly reduce the startup time by ~ 44%! That’s great.
  • I also tried them with our big Dropwizard-based legacy application. Result: 27s startup time without and 17s with the arguments! So we save 10s for each startup or 33%!

IntelliJ IDEA Ultimate provides the dedicated Run Configuration Type “Spring Boot”. If we check the option “Enable launch optimization” (which is the default), the above VM arguments (among others) will be added automatically. But adding the arguments manually is no big deal and can be done in every IDE (like Eclipse) or the community edition of IntelliJ IDEA.

Hot Code Replacement

In reality, even waiting 6 seconds after each code change can be annoying. Moreover, we may need to log in to our (Vaadin) application again (because our session got lost) and navigate to the changed view. This takes time and leads to longer turnaround times. This is where hot code replacement can be extremely handy. This enables us to load the changed code right into our running application.

Basic

The JVM and our IDE (like IntelliJ IDEA or Eclipse) support basic code replacement out of the box. We just have to start the application in the debug mode. Now, we can change some code and re-compile it.

  • In Eclipse we only have to save the file with Ctrl+S
  • In IDEA we have to force a recompilation of the current file with Ctrl+Shift+F9 (or Build > Recompile) or of the whole project with Ctrl+F9 (or Build > Build Project)
Successfully reloaded changed code in IntelliJ IDEA

Successfully reloaded changed code in IntelliJ IDEA

But this approach comes with severe limitations: It only works as long as we are only changing method bodies. This may be sufficient in some cases, for instance when adjusting Vaadin layout code within a method or a REST resource method.

But we can’t change a method signature or a field type. Besides, new methods or fields can’t be hot-replaced. Those changes lead to an error:

Changes like adding or removing methods and fields can't be hot-swapped into the JVM

Changes like adding or removing methods and fields can't be hot-swapped into the JVM

Enhanced

Fortunately, there is Spring-Loaded. It’s a JVM agent that allows reloading classes even after enhanced changes.

Spring Loaded allows you to add/modify/delete methods/fields/constructors. The annotations on types/methods/fields/constructors can also be modified and it is possible to add/remove/change values in enum types.

Although there is “spring” in its name, Spring-Loaded works for every kind of JVM application.

The setup is straightforward:

  • Download the latest artifact from repo.spring.io
  • Add the VM arguments
    -javaagent:springloaded-1.2.9.BUILD-20170831.190856-1.jar -noverify (if the artifact is in the project root) or
    -javaagent:/home/user/development/springloaded-1.2.9.BUILD-20170831.190856-1.jar -noverify (if the artifact is located somewhere else)
  • Start the application in debug mode. Add a method. Hit Ctrl+Shift+F9. Voila, the changes got reloaded!

I already came across situations where the code reloading has failed but most of the time Spring-Loaded works quite well.

Spring-Loaded adds 1 - 2 seconds to the startup time. For me, that’s totally fine because it allows me to avoid many restarts.

How the application has been started RUN DEBUG
Execute main() in the IDE with -noverify -XX:TieredStopAtLevel=1 5.7s 7.3s
Execute main() in the IDE with -noverify -XX:TieredStopAtLevel=1 -javaagent:springloaded.jar 7.1s 8.3s

It should be noted, that the commercial JRebel is an alternative to Spring-Loaded. I haven’t used it yet. So I can’t tell whether JRebel is more powerful and if the fee is justified (especially in times of embedded web servers and fat jars).

Framework-Specific Findings

Spring Boot

Spring provides powerful Developer Tools which can be pretty handy during development. I like to highlight two features: Automatic Restart and LiveReload support.

  • Spring DevTools can automatically restart the application after files on the classpath changes (mind to trigger compilation manually in IDEA with Ctrl+Shift+F9). And this restart is much faster than a cold restart. It takes only 2 - 3 s in my example app. We don’t even need to run the application in debug mode.
  • This is especially nice in conjunction with LiveReload, which automatically triggers a refresh in the browser after the restart. Hint: We have to install a browser extension for this.

However, I don’t use this approach for my Spring Boot+Vaadin applications. I prefer Spring-Loaded for the following reasons:

  • The automatic restart is fast, but it is not as fast as the class reloading with Spring-Loaded. After compilation, the changes take effect immediately. However, we may have to refresh the browser manually, but this depends on the kind of changes (e.g. it’s true for layout changes, but not for logic changes in an event handler).
  • We lose the session. This can be annoying during development because we usually have to log in to our Vaadin application again and navigate again through the application after every change. If we have implemented a remember-me feature anyway or if our Spring Boot application is not stateful, this point may not bother us.
  • From time to time, I get a 404 after an automatic restart. So it doesn’t seem to be 100% stable in conjunction with Vaadin.

Bottom line: The Spring DevTools can be really useful for stateless Spring Boot-based applications. It’s definitely worth to check out! But I stick with Spring-Loaded, which is must faster and preserves the application state.

Vaadin

If Vaadin’s production mode is disabled (vaadin.servlet.production-mode=false), we can utilize on-the-fly theme compilation. You may think that this introduces overhead at runtime and consequently, we should enable the production mode also during development (if we are not working on the theme). Basically, that’s true. The initial request takes about 1 - 2 seconds longer. But afterward, the compiled css file is cached by Vaadin. Subsequent calls are now as fast as with enabled production mode (measured via Chrome Dev Tools). Hence, we can draw the following conclusion: It’s totally safe to keep the production mode disabled (and on-the-fly compilation enabled) during development as it doesn’t add significant overhead after the initial theme compilation.