The heading of this article could very well be the title of a book or even a book series. So, this blog post aims not to boil the ocean but to equip you with the necessary gear to navigate this ocean safely.
Below, you will find three sections dedicated to four key performance indicators: memory consumption, startup time, throughput, and latency. Each section provides an overview of recommendations for improving each indicator and links to additional resources for a deeper dive into the topic.
Table of Contents
How to reduce the startup of Java applications
Slow application startup is irritating per se, but the problem worsens when you restart your services multiple times a day and use cloud services that charge you for compute time. So, for some enterprises, startup time is more critical than other performance indicators.
Java applications are great marathon runners, meaning that they demonstrate good overall performance in the long term. However, they may take several minutes to warm up, i.e., reach stable peak performance. During this period, they process fewer requests and consume more memory, which leads to higher latency at the start and memory overutilization. Different stages of Java start and their impact on overall startup time are described in detail in this article.
Luckily, several approaches tackle the issue of slow Java startup. We wrote several articles dedicated to each of them so you can take a closer look at each solution and choose one that fits your project:
- Class Data Sharing (CDS) is a JVM feature that provides good startup improvement without code refactoring. The article How to use CDS with Spring Boot explains how CDS works and to what extent it may influence startup time.
- Java startup can be reduced to several milliseconds thanks to the Ahead-of-time (AOT) compilation provided by GraalVM Native Image. The main downside of this solution is incompatibility with Java’s dynamic features. It means that you may have to rewrite the code or provide another workaround to befriend Native Image with your app. Follow this guide to using Native Image with Java and solving potential incompatibility issues to get a taste of this technology.
- Another solution that slashes Java startup from minutes to milliseconds is Coordinated Restore at Checkpoint (CRaC). This OpenJDK project enables developers to save a paused Java app to a file and restore it from the file much like a video game. Learn more about operating principles, benefits, and potential pitfalls in the article A guide to CRaC Project.
- One more technology aimed at reducing Java startup time is Project Leyden, currently under development. Project Leyden will rely on CDS and Ahead-of-time compilation to static run-time images. Early access builds of Project Leyden are already available, but not intended to be used in production.
How to optimize Java memory consumption
If you don’t set the memory limits of your Java application, you risk turning it into a glutton that will eat away all available resources and, eventually, your cloud budget. To prevent this from happening or solve the issue if you have already encountered it, follow the tried-and-true recommendations and best practices gathered in the guides below:
- Containerized Java applications tend to get bloated, often containing unnecessary packages or image layers. Find out how to help your Java image lose weight by following a few easy steps from the guide on How to reduce the size of Docker container images.
- Choosing the right base Java image may help you reduce image size twice. Find out how to dockerize a Java application with the smallest base image.
- Adjusting JVM memory limits is an arduous, trial-and-error task. As each use case is unique, writing a guide on how to do that is impossible. However, having a list of key JVM memory configuration options with brief descriptions may be handy.
- Switching to distroless images may be tempting as this technology promises to minimize the footprint of your containerized apps and increase security as a bonus (no Linux distribution, no problems, so to speak). But are there any benefits for Java applications? Read this question-and-answer post on Distroless containers for Java to find out.
- Even seasoned developers can make mistakes when working with containers, which may result in excessive memory consumption and other performance-related issues. Forewarned is forearmed: learn about common containerization pitfalls and ways to to avoid them.
How to improve throughput and latency of Java applications
Numerous factors may affect the latency and throughput of your Java application, from architecture to database interaction or garbage collector implementation. We prepared several guides that will help you get started with identifying and eliminating possible performance issues without diving into fine-tuning:
- A clear picture of code hot spots and performance bottlenecks in your project is necessary for efficient performance improvement. So, you can start with gathering and evaluating application metrics with tried-and-true Java Flight Recorder and Mission Control.
- The Java platform offers a variety of Garbage Collectors tailored to specific purposes and environments. Sometimes, selecting the right GC may help tangibly improve performance without additional adjustments. To find out more about GC mechanisms in Java and available GC implementations, refer to the Guide to Java Garbage Collection.
- The Java Virtual Machine (JVM) implementation may also affect your application's performance. For instance, OpenJ9 is claimed to be more performant than HotSpot. We decided to study HotSpot and OpenJ9 to verify their performance. Check out the HotSpot vs. OpenJ9 study to learn the results.
- Newer Java versions are associated with better overall performance than the legacy ones due to new features and numerous optimizations introduced to the compiler and garbage collections. So, the obvious advice would be to migrate from Java 8 or 11 to Java 17, but many enterprises need more time to complete the migration due to the complexities of the procedure. However, it is possible to bring the performance of newer Java versions to JDK 8 or 11-based projects without upgrading the Java version. Find out how to boost the performance of your application without significant code changes with Liberica JDK Performance Edition 8 and 11.
If you would like to read more articles dedicated to Java performance and guides on using cutting-edge tools with your Java app, subscribe to our newsletter. We will send you a monthly digest of our latest posts!