28.1 CDS Fundamentals and Architecture
Class Data Sharing (CDS) is a JVM feature that significantly improves startup time and reduces memory footprint by pre-processing and sharing class metadata across multiple JVM instances.
What is CDS?
// CDS Fundamentals
public class CDSFundamentals {
public static void printCDSOverview() {
System.out.println("=== CLASS DATA SHARING (CDS) OVERVIEW ===");
System.out.println("\n--- WHAT IS CDS? ---");
System.out.println("\nDefinition:");
System.out.println(" CDS allows class metadata to be shared between");
System.out.println(" multiple JVM processes running the same application.");
System.out.println("\n--- HOW IT WORKS ---");
System.out.println("\n1. Traditional Class Loading (Without CDS):");
System.out.println(" ┌──────────────┐");
System.out.println(" │ JVM #1 │ • Load .class files");
System.out.println(" │ │ • Parse bytecode");
System.out.println(" │ Parse │ • Verify classes");
System.out.println(" │ Verify │ • Create metadata");
System.out.println(" │ Build Meta │ • Store in memory");
System.out.println(" └──────────────┘ Time: ~500ms");
System.out.println(" ");
System.out.println(" ┌──────────────┐");
System.out.println(" │ JVM #2 │ • Repeat all steps");
System.out.println(" │ │ • Duplicate work");
System.out.println(" │ Parse │ • Separate memory");
System.out.println(" │ Verify │");
System.out.println(" │ Build Meta │");
System.out.println(" └──────────────┘ Time: ~500ms");
System.out.println("\n2. With CDS:");
System.out.println(" ┌──────────────┐");
System.out.println(" │ CDS Archive │ • Pre-processed classes");
System.out.println(" │ (.jsa) │ • Ready-to-use metadata");
System.out.println(" │ │ • Memory-mapped file");
System.out.println(" └──────┬───────┘");
System.out.println(" │ mmap");
System.out.println(" ┌───┴────┬────────┐");
System.out.println(" ▼ ▼ ▼");
System.out.println(" JVM #1 JVM #2 JVM #3");
System.out.println(" Time: ~50ms per JVM");
System.out.println(" Memory: Shared across all JVMs");
System.out.println("\n--- KEY BENEFITS ---");
System.out.println("\n1. Faster Startup:");
System.out.println(" • Skip class parsing");
System.out.println(" • Skip verification");
System.out.println(" • Skip metadata construction");
System.out.println(" • Typical improvement: 10-40%");
System.out.println("\n2. Reduced Memory Footprint:");
System.out.println(" • Class metadata shared via memory mapping");
System.out.println(" • One copy in physical RAM");
System.out.println(" • Multiple JVMs reference same pages");
System.out.println(" • Typical savings: 20-50MB per JVM");
System.out.println("\n3. Better Resource Utilization:");
System.out.println(" • Less page cache pressure");
System.out.println(" • Improved CPU cache utilization");
System.out.println(" • Reduced I/O during startup");
}
public static void printCDSArchitecture() {
System.out.println("\n=== CDS ARCHITECTURE ===");
System.out.println("\n--- SHARED ARCHIVE STRUCTURE ---");
System.out.println("\nA .jsa file contains:");
System.out.println("\n1. Class Metadata:");
System.out.println(" • Klass structures (internal representation)");
System.out.println(" • Method metadata");
System.out.println(" • Field information");
System.out.println(" • Constant pool entries");
System.out.println("\n2. Bytecode:");
System.out.println(" • Pre-verified bytecode");
System.out.println(" • Ready for execution");
System.out.println(" • No verification needed");
System.out.println("\n3. String Table:");
System.out.println(" • Interned strings");
System.out.println(" • Shared across JVM instances");
System.out.println("\n4. Symbol Table:");
System.out.println(" • Method names");
System.out.println(" • Field names");
System.out.println(" • Class names");
System.out.println("\n--- MEMORY MAPPING ---");
System.out.println("\nHow CDS uses memory mapping:");
System.out.println("\n1. Archive Loading:");
System.out.println(" • JVM calls mmap() system call");
System.out.println(" • Maps .jsa file into process address space");
System.out.println(" • No physical copy to RAM initially");
System.out.println("\n2. On-Demand Paging:");
System.out.println(" • Pages loaded as accessed");
System.out.println(" • OS handles page faults");
System.out.println(" • Shared pages across processes");
System.out.println("\n3. Copy-on-Write:");
System.out.println(" • Shared pages are read-only");
System.out.println(" • Modifications trigger COW");
System.out.println(" • Modified pages become private");
System.out.println("\n--- METADATA REGIONS ---");
System.out.println("\nCDS archive has multiple regions:");
System.out.println("\nRead-Only (RO) Region:");
System.out.println(" • Class metadata");
System.out.println(" • Method metadata");
System.out.println(" • Immutable data");
System.out.println(" • Fully shareable");
System.out.println("\nRead-Write (RW) Region:");
System.out.println(" • Mutable class data");
System.out.println(" • Initially shared");
System.out.println(" • COW on modification");
System.out.println("\nMetadata (MD) Region:");
System.out.println(" • Symbol table");
System.out.println(" • String table");
System.out.println(" • Dictionary");
}
public static void printDefaultCDS() {
System.out.println("\n=== DEFAULT CDS ARCHIVE ===");
System.out.println("\n--- JDK INCLUDES DEFAULT ARCHIVE ---");
System.out.println("\nSince JDK 12:");
System.out.println(" • Default CDS archive included");
System.out.println(" • Contains JDK core classes");
System.out.println(" • ~1,300 classes from java.base");
System.out.println(" • Enabled by default");
System.out.println("\nLocation:");
System.out.println(" $JAVA_HOME/lib/server/classes.jsa");
System.out.println(" (or classes_nocoops.jsa without compressed oops)");
System.out.println("\n--- DEFAULT ARCHIVE USAGE ---");
System.out.println("\nAutomatic (default):");
System.out.println(" java -Xshare:auto MyApp");
System.out.println(" ");
System.out.println(" Behavior:");
System.out.println(" • Try to use default archive");
System.out.println(" • Fall back if unavailable");
System.out.println(" • Silently disable on mismatch");
System.out.println("\nForce enabled:");
System.out.println(" java -Xshare:on MyApp");
System.out.println(" ");
System.out.println(" Behavior:");
System.out.println(" • Require CDS archive");
System.out.println(" • Fail if not available");
System.out.println(" • Error on mismatch");
System.out.println("\nDisabled:");
System.out.println(" java -Xshare:off MyApp");
System.out.println(" ");
System.out.println(" Behavior:");
System.out.println(" • Don't use CDS");
System.out.println(" • Traditional class loading");
System.out.println(" • Useful for troubleshooting");
System.out.println("\n--- VERIFYING DEFAULT ARCHIVE ---");
System.out.println("\nCheck if CDS is active:");
System.out.println(" java -Xlog:cds -version");
System.out.println("\nExpected output:");
System.out.println(" [info][cds] Opened archive /path/to/classes.jsa");
System.out.println(" [info][cds] Mapped 1234 classes from archive");
System.out.println("\n--- WHAT'S INCLUDED ---");
System.out.println("\nDefault archive contains:");
System.out.println(" • java.lang.* classes");
System.out.println(" • java.util.* classes");
System.out.println(" • java.io.* classes");
System.out.println(" • Other java.base module classes");
System.out.println(" • JDK internal classes");
System.out.println("\nNOT included:");
System.out.println(" • Application classes");
System.out.println(" • Third-party libraries");
System.out.println(" • Other JDK modules (java.sql, etc.)");
System.out.println(" → Use AppCDS for these");
}
}
CDS vs AppCDS
// CDS vs AppCDS Comparison
public class CDSComparison {
public static void printCDSvsAppCDS() {
System.out.println("=== CDS vs APPCDS ===");
System.out.println("\n--- CDS (Class Data Sharing) ---");
System.out.println("\nScope:");
System.out.println(" • JDK classes only");
System.out.println(" • java.base module");
System.out.println(" • ~1,300 core classes");
System.out.println("\nSetup:");
System.out.println(" • Included with JDK");
System.out.println(" • No configuration needed");
System.out.println(" • Enabled by default");
System.out.println("\nBenefit:");
System.out.println(" • Small startup improvement (5-10%)");
System.out.println(" • Modest memory savings");
System.out.println("\n--- APPCDS (Application CDS) ---");
System.out.println("\nScope:");
System.out.println(" • JDK classes");
System.out.println(" • Application classes");
System.out.println(" • Third-party libraries");
System.out.println(" • Custom class loaders");
System.out.println("\nSetup:");
System.out.println(" • Requires archive creation");
System.out.println(" • Training run needed");
System.out.println(" • Manual configuration");
System.out.println("\nBenefit:");
System.out.println(" • Significant startup improvement (20-40%)");
System.out.println(" • Substantial memory savings");
System.out.println(" • Critical for microservices");
System.out.println("\n--- WHEN TO USE EACH ---");
System.out.println("\nUse default CDS (automatic):");
System.out.println(" ✓ Single JVM instance");
System.out.println(" ✓ No special requirements");
System.out.println(" ✓ Quick development/testing");
System.out.println("\nUse AppCDS (manual setup):");
System.out.println(" ✓ Multiple JVM instances");
System.out.println(" ✓ Containerized deployments");
System.out.println(" ✓ Microservices architecture");
System.out.println(" ✓ Startup time critical");
System.out.println(" ✓ Memory constrained");
}
}
When CDS Cannot Be Used
// CDS Limitations
public class CDSLimitations {
public static void printLimitations() {
System.out.println("=== CDS LIMITATIONS ===");
System.out.println("\n--- CONDITIONS THAT DISABLE CDS ---");
System.out.println("\n1. Agent Modifications:");
System.out.println(" -javaagent:agent.jar");
System.out.println(" ");
System.out.println(" • Java agents can modify classes");
System.out.println(" • Shared metadata would be incorrect");
System.out.println(" • CDS automatically disabled");
System.out.println("\n2. Incompatible Flags:");
System.out.println(" -Xverify:all");
System.out.println(" -XX:+UseAppCDS with -Xshare:off");
System.out.println(" ");
System.out.println(" • Some flags conflict with CDS");
System.out.println(" • Full verification incompatible");
System.out.println("\n3. Classpath Mismatch:");
System.out.println(" • Archive created with classpath A");
System.out.println(" • Running with classpath B");
System.out.println(" • Classes don't match");
System.out.println(" • CDS disabled for safety");
System.out.println("\n4. JVM Version Mismatch:");
System.out.println(" • Archive from JDK 17.0.1");
System.out.println(" • Running on JDK 17.0.2");
System.out.println(" • Internal structures changed");
System.out.println(" • Must regenerate archive");
System.out.println("\n--- CLASS LOADER RESTRICTIONS ---");
System.out.println("\nBefore JDK 11:");
System.out.println(" • Only bootstrap class loader");
System.out.println(" • No custom class loaders");
System.out.println(" • No platform class loader");
System.out.println("\nJDK 11+ (AppCDS improvements):");
System.out.println(" • Bootstrap class loader ✓");
System.out.println(" • Platform class loader ✓");
System.out.println(" • System class loader ✓");
System.out.println(" • Custom class loaders ✓ (with setup)");
System.out.println("\n--- BEST PRACTICES ---");
System.out.println("\n✓ DO:");
System.out.println(" • Regenerate after JDK updates");
System.out.println(" • Regenerate after classpath changes");
System.out.println(" • Test with -Xshare:on to catch issues");
System.out.println(" • Document archive creation process");
System.out.println("\n✗ DON'T:");
System.out.println(" • Reuse archive across JDK versions");
System.out.println(" • Ignore -Xshare:on failures");
System.out.println(" • Use with bytecode instrumentation");
System.out.println(" • Share archive across different apps");
}
}
Measuring CDS Impact
// Measuring CDS Benefits
public class CDSMeasurement {
public static void measureStartupTime() {
System.out.println("=== MEASURING STARTUP TIME ===");
System.out.println("\n--- WITHOUT CDS ---");
System.out.println("time java -Xshare:off -cp app.jar com.example.Main");
System.out.println("Result: 2.5 seconds");
System.out.println("\n--- WITH CDS ---");
System.out.println("time java -Xshare:on -XX:SharedArchiveFile=app.jsa \\");
System.out.println(" -cp app.jar com.example.Main");
System.out.println("Result: 1.8 seconds");
System.out.println("\nImprovement: 28% faster startup");
}
public static void measureMemoryUsage() {
System.out.println("\n=== MEASURING MEMORY FOOTPRINT ===");
System.out.println("\n--- SINGLE JVM (no benefit) ---");
System.out.println("\nWithout CDS:");
System.out.println(" Resident memory: 120 MB");
System.out.println("\nWith CDS:");
System.out.println(" Resident memory: 118 MB");
System.out.println(" (Minimal difference for single instance)");
System.out.println("\n--- MULTIPLE JVMS (significant benefit) ---");
System.out.println("\n5 JVMs without CDS:");
System.out.println(" Per-process: 120 MB × 5 = 600 MB");
System.out.println(" Shared: ~50 MB");
System.out.println(" Total physical RAM: ~550 MB");
System.out.println("\n5 JVMs with CDS:");
System.out.println(" Per-process: 105 MB × 5 = 525 MB");
System.out.println(" Shared: ~90 MB (CDS archive + text)");
System.out.println(" Total physical RAM: ~435 MB");
System.out.println("\nSavings: 115 MB (21% reduction)");
System.out.println("\n--- MEASUREMENT TOOLS ---");
System.out.println("\nLinux:");
System.out.println(" ps -o pid,rss,vsz,comm -p <pid>");
System.out.println(" pmap -x <pid> | grep jsa");
System.out.println(" cat /proc/<pid>/smaps | grep -A 15 classes.jsa");
System.out.println("\nmacOS:");
System.out.println(" vmmap <pid> | grep classes.jsa");
System.out.println("\nJVM flags:");
System.out.println(" -Xlog:cds+map=trace");
System.out.println(" Shows memory region mapping");
}
}
Best Practices
- Use default CDS: Enabled automatically, provides baseline benefits.
- Consider AppCDS for microservices: Significant gains with multiple instances.
- Regenerate after updates: Archive tied to specific JDK version and classpath.
- Test with -Xshare:on: Catch configuration issues early.
- Monitor with logging: Use -Xlog:cds to verify archive usage.
- Combine with other optimizations: CDS + AOT + profile-guided optimization.
- Document creation process: Automate archive generation in CI/CD.
- Measure actual impact: Benchmark startup time and memory in your environment.
- Include in container images: Pre-generate archive during image build.
- Keep archives small: Only include frequently-used classes.