posts
How to use Project Leyden with Spring Boot

How to use Project Leyden with Spring Boot

Jul 1, 2024
Catherine Edelveis
9.8

Project Leyden Early Access builds are already available, so the time for experiments has come! You can follow the instructions in the README.md of the Project’s release notes and run the benchmark provided by the team. But I was also curious to see Leyden in action with Spring Boot.

Let’s couple Project Leyden with our favorite framework and see what startup gains we can already achieve.

IMPORTANT NOTE: Project Leyden EA builds are based on experimental code and are not meant for production use. In addition, some features in the EA builds may be changed or removed, and the workflow may also change. So, this article will be updated accordingly in the future.

By the way, did you know that you can reduce the RAM consumption of your Spring Boot services by up to 30%? Discover Alpaquita Containers tailor-made for Spring Boot. Buidpacks are also available!

What is Project Leyden?

Project Leyden is an OpenJDK project that has been in the making since 2022. The project aims to leverage CDS (Class Data Sharing) and Ahead-of-Time optimizations to selectively shift and constrain computations from runtime to some point in time — to an earlier phase, for instance.

The ultimate goal is to create fully static images under the closed-world constraint. The closed-world constraint enables more powerful startup optimizations, as we can see with GraalVM Native Image. But it doesn’t go well with Java’s dynamism. So, currently, the Project Leyden team explores weaker constraints that can be applicable to a wider range of applications.

What can Project Leyden do at this stage of its development? It observes application behavior during the trial run and performs certain computations based on the observations. After the trial run, it creates a CDS archive with class metadata, method profilers, and compiled code (such as methods frequently used during the trial run). As a result, when you perform a production run of your application, it uses the data from the CDS archive and starts faster. How much faster? Let’s find out!

Use Project Leyden with Spring Boot

For my experiment, I took Spring Petclinic for Spring Boot 3.3. It starts in about 3.5 seconds on my machine, an old M1 MacBook Air. The framework version is important because of first-class CDS support and CDS-friendly unpacked deployment (more on it below). 

First, create a jar with the following command:

mvn -Dmaven.test.skip=true clean package

Then, create an exploded jar, which is recommended to be used in production by Spring:

java -Djarmode=tools -jar target/spring-petclinic-3.3.0-SNAPSHOT.jar extract

When you use the -Djarmode=tools utility, Spring extracts the application into a directory using various layouts. In the case of a default layout, the directory will contain

  • a lib subdirectory with libraries, and 
  • the application JAR with application classes and a manifest that references libraries in the lib folder.

Using this layout with non-nested jars will enable you to benefit more from Leyden capabilities.

Now, let’s make use of Leyden. We need to conduct a trial run:

java -XX:CacheDataStore=SpringPetclinic.cds -jar spring-petclinic-3.3.0-SNAPSHOT/spring-petclinic-3.3.0-SNAPSHOT.jar

Two files will be created:

  • SpringPetclinic.cds with class metadata, heap objects, and profiling data;
  • SpringPetclinic.cds.code with AOT-compiled methods (it is planned to merge this file with SpringPetclinic.cds) in the future.

After that, conduct a production run of the application with the same command:

java -XX:CacheDataStore=SpringPetclinic.cds -jar spring-petclinic-3.3.0-SNAPSHOT/spring-petclinic-3.3.0-SNAPSHOT.jar

              |\      _,,,--,,_
             /,`.-'`'   ._  \-;;,_
  _______ __|,4-  ) )_   .;.(__`'-'__     ___ __    _ ___ _______
 |       | '---''(_/._)-'(_\_)   |   |   |   |  |  | |   |       |
 |    _  |    ___|_     _|       |   |   |   |   |_| |   |       | __ _ _
 |   |_| |   |___  |   | |       |   |   |   |       |   |       | \ \ \ \
 |    ___|    ___| |   | |      _|   |___|   |  _    |   |      _|  \ \ \ \
 |   |   |   |___  |   | |     |_|       |   | | |   |   |     |_    ) ) ) )
 |___|   |_______| |___| |_______|_______|___|_|  |__|___|_______|  / / / /
 ==================================================================/_/_/_/
:: Built with Spring Boot :: 3.3.0
2024-06-29T11:02:16.569+03:00  INFO 11092 --- [           main] o.s.s.petclinic.PetClinicApplication     : Starting PetClinicApplication v3.3.0-SNAPSHOT using Java 24-leydenpremain with PID 11092 (/Users/ekaterina/Downloads/spring-petclinic-main/spring-petclinic-3.3.0-SNAPSHOT/spring-petclinic-3.3.0-SNAPSHOT.jar started by ekaterina in /Users/ekaterina/Downloads/spring-petclinic-main)
2024-06-29T11:02:16.571+03:00  INFO 11092 --- [           main] o.s.s.petclinic.PetClinicApplication     : No active profile set, falling back to 1 default profile: "default"
2024-06-29T11:02:16.770+03:00  INFO 11092 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2024-06-29T11:02:16.774+03:00  INFO 11092 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 4 ms. Found 2 JPA repository interfaces.
2024-06-29T11:02:16.934+03:00  INFO 11092 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-06-29T11:02:16.936+03:00  INFO 11092 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-06-29T11:02:16.936+03:00  INFO 11092 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.24]
2024-06-29T11:02:16.942+03:00  INFO 11092 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-06-29T11:02:16.942+03:00  INFO 11092 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 365 ms
2024-06-29T11:02:17.019+03:00  INFO 11092 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-06-29T11:02:17.029+03:00  INFO 11092 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:07c35cd0-80c3-40fc-9bd5-34a0c390afff user=SA
2024-06-29T11:02:17.030+03:00  INFO 11092 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-06-29T11:02:17.069+03:00  INFO 11092 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-06-29T11:02:17.071+03:00  INFO 11092 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.5.2.Final
2024-06-29T11:02:17.073+03:00  INFO 11092 --- [           main] o.h.c.internal.RegionFactoryInitiator    : HHH000026: Second-level cache disabled
2024-06-29T11:02:17.090+03:00  INFO 11092 --- [           main] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
2024-06-29T11:02:17.179+03:00  INFO 11092 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2024-06-29T11:02:17.179+03:00  INFO 11092 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2024-06-29T11:02:17.220+03:00  INFO 11092 --- [           main] o.s.d.j.r.query.QueryEnhancerFactory     : Hibernate is in classpath; If applicable, HQL parser will be used.
2024-06-29T11:02:17.490+03:00  INFO 11092 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 14 endpoints beneath base path '/actuator'
2024-06-29T11:02:17.525+03:00  INFO 11092 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-06-29T11:02:17.536+03:00  INFO 11092 --- [           main] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 1.01 seconds (process running for 2.429)

As you can see, the application started in 1 second, which is more than three times faster than without Leyden!

Looking into the logs

If you are curious to know what happens behind the scenes of Project Leyden, logging will help. So, let’s conduct two more trial and production runs with enabled logging.

Note that before creating new .cds* files, the ones generated from previous trial runs should be deleted:

rm -fv SpringPetclinic.cds*

Alright, let’s conduct a trial run and log the process of the CDS archive creation with the -Xlog:cds=debug:file=log/cds.log option:

java -XX:CacheDataStore=SpringPetclinic.cds -Xlog:cds=debug:file=log/cds.log -Dspring.context.exit=onRefresh -jar spring-petclinic-3.3.0-SNAPSHOT/spring-petclinic-3.3.0-SNAPSHOT.jar

Now, modify the command for launching a product run to log the number of loaded classes with -Xlog:class+load=info:file=log/class-load.log

java -XX:CacheDataStore=SpringPetclinic.cds -Xlog:class+load=info:file=log/class-load.log -jar spring-petclinic-3.3.0-SNAPSHOT/spring-petclinic-3.3.0-SNAPSHOT.jar

To see how many classes were loaded in total, run:

cat log/class-load.log | wc -l
17375

This file also contains data on classes loaded from the shared archive. Run the following command: 

grep -o 'source: shared' -c log/class-load.log
16733

As you can see, 96% of classes got into the archive! But Leyden is not only about loading classes. As mentioned above, the archive also stores profiling data and compiled code. More detailed information on the archive generation can be found in the cds.log file.

Conclusion

Project Leyden is flourishing. The preliminary results in startup and warmup times are tangible, and more is yet to come! We at BellSoft are looking forward to the stable release. But what if you need to reduce startup now?

  • Take a look at CDS. It is a production-ready feature that enables you to reduce startup by up to 54% depending on the setup. Most JDK distributions, including Liberica JDK, are provided with a pre-packaged CDS archive, and using it is pretty straightforward. Read more in the article How to use CDS with Spring Boot.
  • GraalVM Native Image enables the generation of native images that start up almost instantly, but may require code refactoring. Find out more in Spring Boot with GraalVM Native Image.
  • Project CRaC is an OpenJDK API that enables you to reduce Java application startup to milliseconds and preserve the power of JIT for dynamic performance optimization. Learn more about this solution in What is CRaC? article.

Want to know more about improving the performance of your Java apps? Subscribe to our newsletter!

 

Subcribe to our newsletter

figure

Read the industry news, receive solutions to your problems, and find the ways to save money.

Further reading