Optimización de Java con AppCDS
En esta ocasión, nuestros Hunters nos muestran cómo optimizar aplicaciones Java con AppCDS. Esta tecnología permite compartir clases precargadas entre instancias para mejorar el tiempo de arranque y reducir el uso de memoria.
Optimización de Java con AppCDS
En el desarrollo de aplicaciones en Java, resultan especialmente relevantes el tiempo de arranque y el consumo de memoria, especialmente en entornos de producción o en arquitecturas con múltiples microservicios. Por eso, vamos a hablar de una herramienta poco conocida pero muy útil: Application Class-Data Sharing (AppCDS).
¿Qué es AppCDS?
AppCDS es una tecnología incluida en la máquina virtual de Java (JVM) que permite compartir información sobre clases Java ya cargadas entre varias instancias de una aplicación. Esto significa que cuando arrancamos una nueva instancia de la aplicación, puede reutilizar esa información en lugar de volver a cargarla desde cero.
Este proceso se basa en la creación de un archivo de caché, que guarda los metadato de las clases que se usaron en una ejecución anterior. Las siguientes veces que arranque la app, ese archivo se puede aprovechar para arrancar más rápido y usar menos memoria.
Beneficios principales
• Arranques más rápidos: Muy útil en microservicios que se escalan dinámicamente (se crean o eliminan réplicas constantemente).• Ahorro de memoria: Como varias instancias de Java comparten el archivo, se evita tener copias repetidas de la misma información.
• No afecta al código: No necesitas hacer cambios en la lógica de tu aplicación para usarlo.
¿Por qué es especialmente interesante en Kubernetes?
AppCDS encuentra uno de sus mejores escenarios en entornos Kubernetes, donde los contenedores y microservicios se levantan y escalan dinámicamente.
Tiempos de arranque reducidos en escalados horizontales
En Kubernetes, los pods pueden levantarse o replicarse en cuestión de segundos para manejar una carga creciente. Usar AppCDS permite que estas nuevas instancias de microservicios Java arranquen más rápido al evitar el análisis y carga inicial de un numeroalto de clases.
Esto es especialmente útil en arquitecturas autoscalables, donde el cold start puede ser un cuello de botella tanto en rendimiento como en experiencia de usuario.
Contenedores más ligeros y predecibles
AppCDS permite que los contenedores Java se comporten de forma más uniforme en cuanto a consumo de CPU y memoria en el arranque. Al contener un archivo de caché de clases en la imagen base del contenedor, se mejora la previsibilidad del comportamiento de las instancias, lo cual es vital en despliegues masivos y CI/CD.
Perfecto para despliegues reproducibles
Durante el proceso de creación de una imagen Docker para tu aplicación Java, es posible generar un archivo especial que guarda información sobre cómo se cargan las clases. Este archivo, conocido como caché AppCDS, se incluye dentro de la imagen, por lo que todos los contenedores que se creen a partir de ella usarán la misma base. Esto permite que la aplicación arranque de forma más rápida y consistente, tanto en desarrollo como en pruebas o producción.
Ejemplo de caso real
Una empresa que ejecuta 20 microservicios Java sobre Kubernetes, cada uno con múltiples réplicas, puede ahorrar varios segundos en cada cold start al usar AppCDS. En despliegues tipo autoscaling, esto se traduce en mejor tiempo de respuesta, menor presión en el clúster y ahorro en recursos.
Demo: Usando AppCDS con Java y Docker en Kubernetes
Esta demostración tiene como propósito mostrar cómo se puede configurar y utilizar la funcionalidad Class Data Sharing (AppCDS) de Java dentro de un contenedor Docker, y cómo desplegar ese contenedor en un clúster de Kubernetes para mejorar el tiempo de arranque de una aplicación Java.
Durante la demostración, se siguen los siguientes pasos:.
- Creación de una aplicación Java simple usando Spring Boot.
- Construcción de una imagen Docker con y sin AppCDS.
- Despliegue de la aplicación en Kubernetes.
- Medición y comparación de los tiempos de arranque con y sin AppCDS.
Este proceso permite observar de forma práctica cómo AppCDS puede contribuir a mejorar el rendimiento en aplicaciones Java que se ejecutan en entornos basados en contenedores.
Paso 1: Crear una Aplicación Java
En este paso, se crea una aplicación Java sencilla utilizando un framework como Spring Boot. Esta aplicación servirá como base para la demostración y se empaquetará en un archivo .jar, que posteriormente se usará para construir las imágenes Docker.

DemoApplication

HelloController
Paso 2: Crear la Imagen Docker
Una vez creada la aplicación, se debe acceder al directorio donde se encuentran el Dockerfile y el archivo .jar generado. Primero, se ejecuta el siguiente comando para generar dicho archivo:
mvn clean package
Con el archivo .jar listo, se procede a construir dos imágenes Docker: una que incluye AppCDS y otra sin esta funcionalidad. En el caso de AppCDS se incluye unos pasos mas siendo el mas importante en el que se hace uso de -XX:ArchiveClassesAtExit=appcds.jsa junto con XX:SharedClassListFile=classes.lst para generar un archivo JSA (Java Shared Archive).

Dockerfile con AppCDS

Dockerfile sin AppCDS

docker build -t demo-app:cds .
Para la versión sin AppCDS, se debe crear un Dockerfile.noappcds con la configuración
correspondiente (es decir, sin la activación de AppCDS) y luego construir la imagen con:

docker build -f Dockerfile.noappcds -t demo-app:without-cds .
Las dos imágenes Docker, demo-app:cds y demo-app:without-cds, se diferencian en la activación de AppCDS (Class Data Sharing):
- demo-app:cds: Usa AppCDS para precargar clases de la aplicación, lo que acelera el tiempo de arranque y optimiza el uso de memoria. Esto se logra al generar un archivo con las clases precompiladas y configurando la JVM para usarlo.
- demo-app:without-cds: No utiliza AppCDS. En su lugar, las clases se cargan y compilan en tiempo de ejecución, lo que resulta en un mayor tiempo de arranque y uso de memori.

Tiempo de construcción de la imagen
El tiempo de construcción de la imagen es diferente en ambos supuestos, esto es debido a que la construcción con AppCDS incluye varios pasos mas. En este supuesto la diferencia la podemos ver en esos 3s que tarda en generar las clases. En aplicaciones mas grandes habria que contrastar la diferencia de tiempo de uno u otro proceso para tenerlo en cuenta para su uso.
Otra diferencia que se puede apreciar es el tamaño de la imagen, siendo la imagen con AppCDS mayor.

Tamaño de la imagen construida
La diferencia clave en los Dockerfiles es la inclusión de pasos específicos para crear el archivo appcds en la imagen con AppCDS, mientras que en la imagen sin AppCDS, estos pasos no están presentes.
Paso 3: Desplegar en Kubernetes
Para ejecutar el contenedor en un clúster de Kubernetes local, se emplea Minikube, una herramienta que permite gestionar un entorno de Kubernetes en una única máquina. Esta configuración resulta adecuada para pruebas y entornos de desarrollo, sin requerir un clúster distribuido.


Este archivo configura un Deployment con cuatro réplicas de la aplicación, junto con un Service para exponerla en Kubernetes. El despliegue utiliza una imagen que incorpora AppCDS. Para evaluar el rendimiento sin esta optimización, se debe utilizar la imagen generada sin AppCDS.
Paso 4: Aplicar el Despliegue en Kubernetes
Aplica el archivo YAML para desplegar la aplicación en Kubernetes:

kubectl apply -f deployment.yaml
Se verifica que todo esté funcionando:

kubectl get pods

kubectl get svc
Paso 5: Comparar los Tiempos de Arranque
La comparación de los tiempos de arranque puede realizarse mediante el análisis de los registros de los pods, identificando las líneas que informan sobre el tiempo de inicio de la aplicación.
- Observar los logs de la aplicación usando:
kubectl logs <nombre-del-pod>
Se localiza la línea que muestra el tiempo de arranque

Logs de un pod corriendo la aplicación con AppCDS
2. Verificar los tiempos de transición del pod usando:
Usando los logs, mediante un script se recupera esa línea de cada pod para obtener el tiempo de inicio y comparar los pods del deployment con AppCDS y los que no

Conclusiones de la demo con AppCDS
Tras obtener los tiempos de inicio de las diferentes replicas, se obtienen los siguientes resultados:

- Los pods que utilizan AppCDS (demo-appcds-deployment-*) presentan tiempos de arranque consistentemente más bajos que los que no lo utilizan.
- La diferencia promedio observada oscila entre 4 y 10 segundos por pod.
- Esto representa una mejora de entre 15 % y 25 % en el tiempo de arranque.
El uso de AppCDS en entornos Java dentro de Kubernetes puede suponer una mejora notable en los tiempos de arranque de aplicaciones, especialmente en despliegues con múltiples réplicas o en arquitecturas altamente escalables. Esta optimización puede ser especialmente útil en escenarios de autoescalado, donde el tiempo de inicialización de nuevos pods tiene un impacto directo en la experiencia de usuario o el rendimiento
general del sistema

Patricio Flores
Técnico de Software
Altia