One of the key challenges for Java applications has always been startup performance and memory efficiency. Unlike native binaries, JVM applications require class loading, verification, and JIT compilation before execution begins. This overhead impacts short-lived applications, microservices, and serverless workloads.
To address this, the JVM introduced Class Data Sharing (CDS) and its extension Application Class Data Sharing (AppCDS). These features enable multiple JVM processes to share preloaded class metadata, reducing startup time and memory usage across JVM instances.
This tutorial dives into how CDS and AppCDS work, their evolution, and how developers can leverage them for production-grade performance.
What is Class Data Sharing (CDS)?
Definition
Class Data Sharing (CDS) is a JVM feature that stores class metadata in a shared archive file. Multiple JVM processes can map this file into memory, allowing them to reuse class metadata instead of duplicating it.
Benefits
- Faster Startup: Preloaded classes reduce class loading overhead.
- Lower Memory Footprint: Class metadata shared across JVM processes.
- Improved Scalability: Multiple JVMs running on the same host consume less memory.
How CDS Works
- Dump Phase: JVM runs with a flag to dump a shared archive containing core Java classes.
- Archive Mapping: On startup, JVM maps the archive into memory.
- Class Reuse: Shared metadata reduces redundant class loading and verification.
Example Command (Java 8)
java -Xshare:dump
java -Xshare:on -version
What is AppCDS?
Application Class Data Sharing (AppCDS) extends CDS to include application and third-party classes in the shared archive.
Benefits
- Startup improvements for frameworks like Spring, Hibernate, and Tomcat.
- Reduced memory for microservices running multiple JVMs.
- Works well in containerized environments.
Example Commands
# Create archive
java -Xshare:dump -XX:SharedClassListFile=classes.lst -XX:SharedArchiveFile=appcds.jsa -cp myapp.jar
# Run with AppCDS
java -Xshare:on -XX:SharedArchiveFile=appcds.jsa -cp myapp.jar MyMainClass
CDS vs AppCDS
Feature | CDS (Core) | AppCDS (Extended) |
---|---|---|
Scope | Bootstrap classes only | Application + third-party |
Startup Gain | Moderate | High |
Memory Savings | Significant | Even greater |
Use Case | Any JVM app | Large apps, microservices |
Monitoring and Tuning CDS/AppCDS
Flags
-Xshare:on
→ Enables class sharing.-Xshare:off
→ Disables class sharing.-XX:+UseAppCDS
(Java 8/9).- From Java 10+, AppCDS is included by default.
Tools
- JFR + JMC: Monitor startup behavior.
- GC Logs: Compare heap usage with and without CDS.
- Container Metrics: Validate memory savings in Kubernetes/Docker.
Real-World Case Studies
Case 1: Microservices on Kubernetes
- Issue: Slow startup under autoscaling.
- Solution: Enabled AppCDS with Spring Boot classes.
- Result: 25% faster startup, lower pod memory usage.
Case 2: High-Density JVM Hosting
- Issue: Hundreds of JVMs on the same machine consumed excess memory.
- Solution: CDS enabled for bootstrap classes.
- Result: Memory savings of ~100MB per JVM.
Case 3: Serverless Functions
- Issue: Cold start latency.
- Solution: AppCDS preloaded application classes.
- Result: Noticeable reduction in cold start time.
Pitfalls and Troubleshooting
- Version Mismatch: Archive tied to specific JDK version.
- Dynamic Class Loading: Classes loaded at runtime not included in archive.
- Complex Build Pipelines: Requires additional steps for archive generation.
- Containers: Must ensure consistent JDK version across nodes.
Best Practices
- Always enable CDS (default in modern JVMs).
- Use AppCDS for microservices, frameworks, and serverless apps.
- Integrate AppCDS archive generation in CI/CD pipelines.
- Benchmark startup and memory usage before/after enabling.
- Combine with AOT compilation (GraalVM native-image) for ultra-low startup latency.
JVM Version Tracker
- Java 8: CDS available, AppCDS commercial in Oracle JDK.
- Java 9: AppCDS introduced (commercial).
- Java 10: AppCDS open-sourced and available to all.
- Java 11+: CDS/AppCDS enabled by default.
- Java 17+: ZGC/Shenandoah integrated well with AppCDS.
- Java 21+: Improved CDS support, better NUMA awareness.
Conclusion & Key Takeaways
- CDS improves startup and reduces memory by sharing core classes.
- AppCDS extends this to application classes for even greater benefits.
- Critical for microservices, serverless functions, and multi-JVM environments.
- Should be part of every modern Java deployment pipeline.
FAQ
1. What is the JVM memory model and why does it matter?
It ensures correctness across threads, crucial when sharing class metadata.
2. How does G1 GC differ from CMS?
G1 uses region compaction, while CMS fragmented memory.
3. When should I use ZGC or Shenandoah?
When low-latency and large heaps are required.
4. What are JVM safepoints and why do they matter?
They ensure consistency during GC, JIT, and CDS/AppCDS mapping.
5. How do I solve OutOfMemoryError in production?
Check GC logs, tune heap, and ensure AppCDS is used effectively.
6. What are the trade-offs of throughput vs latency tuning?
Throughput favors efficiency; latency ensures predictable response.
7. How do I read and interpret GC logs?
Look for pause durations, heap usage before/after, and GC frequency.
8. How does JIT compilation optimize performance?
By compiling hot methods into native code, reducing interpretation overhead.
9. What’s the future of GC in Java (Project Lilliput)?
Smaller headers will reduce memory and improve CDS effectiveness.
10. How does GC differ in microservices vs monoliths?
Microservices prioritize startup/latency; monoliths often prioritize throughput.