1

How to optimize Java startup and runtime performance

 4 weeks ago
source link: https://www.pluralsight.com/resources/blog/software-development/optimize-java-startup-runtime-performance
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

In this article, we’ll look at the state of performance optimization in the Java ecosystem. Specifically, we’ll look at older technologies that are coming of age like AppCDS, and newer technologies like compiling natively with GraalVM and taking warmed snapshots with CrAC. By the end, you’ll better understand the differences between each technology and what performance issues each seeks to solve.

An Overview of Java’s Performance Concerns

While most of your performance challenges are likely due to I/O, network, or your application logic, Java has received a reputation over the years for being slower than other programming languages mainly due to two design decisions: Performing Garbage Collection and Interpreting Bytecode.

In the last decade, substantial effort has been undertaken by the Java community to create increasingly efficient garbage collectors, develop tools for analyzing memory management, and evangelize collection-friendly practices for developers to follow. Fantastic progress has been made, and today these conversations are an acknowledgment of the security benefits of memory management over remaining operational costs.

Java still is on the hook, though, for running through an interpreter, a necessary constraint to ensure portability. One issue is how long it can take the JVM to warm up as it loads and initializes its classpath. Another is the fact that having a layer of abstraction will always be inherently slower than talking directly to the operating system.

And while there are (link to javap article)tools like javap(/link) that one can use to try and optimize your bytecode, such does not easily scale to dozens or hundreds of individual microservices that interoperate. As that architectural pattern gains more purchase in the industry, Java has introduced cross-cutting technologies that allow you to take snapshots of the JVM warmup step or to bypass the interpreter altogether. These allow you to get the same performance characteristics of system languages like C, Go, and Rust.

So let’s jump in and take a look at three common strategies--AppCDS, native compilation, and CrAC--and see how they compare. In each, we’ll use Spring’s PetClinic, an application that is often used in the Java community for benchmarks of a non-trivial Java application.

Capturing a baseline

The two metrics that will be of most interest to us for comparison are PetClinic’s average startup time and its average request time. I ran PetClinic on a Dell Precision 5560, Intel i7, Ubuntu 22.04 machine and achieved an average startup time of 8.12 seconds and an average request time of 0.012 seconds.

Working with AppCDS

As you already know from earlier, one cost across all Java applications is the time needed for the classloader to load classes and initialize them. In 2004, Java introduced a commercial offering called CDS which provided a mechanism for storing loaded classes on the filesystem in such a way that they can be shared between JVM instances. This was made freely available in 2018 with JDK 10, and then further simplified in JDK 13.

To use AppCDS, you must first record your application in use so that the AppCDS archive can be built following the classes that your application uses. You can achieve this by adding the -XX:ArchiveClassesAtExit property like so:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK