
Optimizing Java with AppCDS
When developing Java applications, startup time and memory usage are particularly important, especially in production environments or in architectures with multiple microservices. Therefore, we're going to talk about a little-known but very useful tool: Application Class-Data Sharing (AppCDS).
What is AppCDS?
AppCDS is a technology built into the Java Virtual Machine (JVM) that allows information about already loaded Java classes to be shared between multiple instances of an application. This means that when we start a new instance of the application, it can reuse that information instead of reloading it from scratch.
This process relies on creating a cache file, which stores the metadata of the classes used in a previous run. The next time the app is launched, this file can be used to launch faster and use less memory.
Main benefits
- Faster startups: Very useful in microservices that scale dynamically (replicas are constantly being created or deleted).
- Memory savings : Since multiple Java instances share the file, you avoid having duplicate copies of the same information.
- No code affected : You don’t need to make any changes to your application logic to use it.
Why is it especially interesting in Kubernetes?
AppCDS finds one of its best scenarios in Kubernetes environments, where containers and microservices are dynamically upscaled.
Reduced startup times for horizontal scaling
In Kubernetes, pods can be launched or replicated in seconds to handle increasing load. Using AppCDS allows these new Java microservice instances to start faster by avoiding the initial parsing and loading of a large number of classes.
This is especially useful in autoscaling architectures, where cold start can be a bottleneck in both performance and user experience.
Lighter and more predictable containers
AppCDS enables Java containers to behave more consistently in terms of CPU and memory usage at startup. By including a class cache file in the container's base image, the predictability of instance behavior is improved, which is vital for mass deployments and CI/CD.
Perfect for reproducible deployments
During the process of creating a Docker image for your Java application, you can generate a special file that stores information about how classes are loaded. This file, known as the AppCDS cache, is included within the image, so all containers created from it will use the same base. This allows the application to start more quickly and consistently, whether in development, testing, or production.
Real case example
A company running 20 Java microservices on Kubernetes, each with multiple replicas, can save several seconds on each cold start by using AppCDS. In autoscaling deployments, this translates into improved response times, reduced cluster pressure, and resource savings.
Demo: Using AppCDS with Java and Docker on Kubernetes
This demo aims to show how to configure and use Java Class Data Sharing (AppCDS) functionality within a Docker container, and how to deploy that container to a Kubernetes cluster to improve the startup time of a Java application.
During the demonstration, the following steps are followed:
- Creating a simple Java application using Spring Boot.
- Building a Docker image with and without AppCDS.
- Deploying the application on Kubernetes.
- Measurement and comparison of boot times with and without AppCDS.
This process provides a practical example of how AppCDS can contribute to improved performance in Java applications running in container-based environments.
Step 1: Create a Java Application
In this step, you create a simple Java application using a framework like Spring Boot. This application will serve as the basis for the demonstration and will be packaged into a .jar file, which will later be used to build the Docker images.

DemoApplication

HelloControler
Step 2: Create the Docker Image
Once the application is created, you must navigate to the directory where the Dockerfile and the generated .jar file are located. First, run the following command to generate the file:
mvn clean package
With the .jar file ready, we proceed to build two Docker images: one that includes AppCDS and one without. In the case of AppCDS, a few additional steps are included, the most important of which is using XX:ArchiveClassesAtExit=appcds.jsa along with XX:SharedClassListFile=classes.lst to generate a JSA (Java Shared Archive) file.

Dockerfile with AppCDS

Dockerfile without AppsCDS

docker build demo-app:cds

docker build -f Dockerfile.noappcds -t demo-app:without-cds
The two Docker images, demo-app:cds and demo-app:without-cds, differ in the activation of AppCDS (Class Data Sharing):
- demo-app:cds: Uses AppCDS to preload application classes, which speeds up startup and optimizes memory usage. This is achieved by generating a file with precompiled classes and configuring the JVM to use it.
- demo-app:without-cds: Does not use AppCDS. Instead, classes are loaded and compiled at runtime, resulting in increased startup time and memory usage.

The image build time is different in both cases, as building with AppCDS involves several additional steps. In this case, the difference can be seen in the 3 seconds it takes to generate the classes. In larger applications, the difference in time between the two processes would need to be compared to take into account when using them.
Another difference that can be seen is the size of the image, the image with AppCDS being larger.

The key difference in the Dockerfiles is the inclusion of specific steps to create the appcds file in the image with AppCDS, while in the image without AppCDS, these steps are not present.
Step 3: Deploy to Kubernetes
To run the container in a local Kubernetes cluster, Minikube is used, a tool that allows you to manage a Kubernetes environment on a single machine. This configuration is suitable for testing and development environments and does not require a distributed cluster.


Step 4: Apply the Deployment to Kubernetes
Apply the YAML file to deploy the application to Kubernetes:

Check that everything is working:


Step 5: Compare Boot Times
Comparing startup times can be done by analyzing pod logs, identifying lines that report application startup times.
1.Observe the application logs using
kubectl logs
The line showing the boot time is located.

Logs of a pod running the application with AppCDS
2. Verify pod transition times: Using the logs, a script retrieves that line from each pod to obtain the startup time and compare the pods in the deployment with AppCDS and those without.

Conclusions from the AppCDS demo
After obtaining the start times of the different replicas, the following results are obtained:

- Pods using AppCDS (demo-appcds-deployment-*) have consistently lower startup times than those without.
- The average difference observed ranges from 4 to 10 seconds per pod.
- This represents an improvement of between 15% and 25% in boot time.
Using AppCDS in Java environments within Kubernetes can significantly improve application startup times, especially in multi-replica deployments or highly scalable architectures. This optimization can be especially useful in autoscaling scenarios, where the initialization time of new pods has a direct impact on the user experience or overall system performance.

Patricio Flores
Software Technician
Altia