28.2 Creating and Managing CDS Archives

AppCDS (Application Class Data Sharing) allows you to create custom archives that include application and library classes beyond the default JDK classes.

Static Archive Creation (Traditional AppCDS)

// Static Archive Creation Process
public class StaticArchiveCreation {

    public static void printStaticProcess() {
        System.out.println("=== STATIC APPCDS ARCHIVE CREATION ===");

        System.out.println("\n--- THREE-STEP PROCESS ---");

        System.out.println("\n┌─────────────────────────────────────────┐");
        System.out.println("│ STEP 1: TRAINING RUN                    │");
        System.out.println("│ Collect classes loaded during execution │");
        System.out.println("└─────────────────────────────────────────┘");

        System.out.println("\nCommand:");
        System.out.println("  java -Xshare:off \\");
        System.out.println("       -XX:DumpLoadedClassList=classes.lst \\");
        System.out.println("       -cp app.jar:lib/* \\");
        System.out.println("       com.example.Main");

        System.out.println("\nWhat happens:");
        System.out.println("  • JVM runs application normally");
        System.out.println("  • Tracks every class loaded");
        System.out.println("  • Writes class names to classes.lst");
        System.out.println("  • File format: one class per line");

        System.out.println("\nExample classes.lst:");
        System.out.println("  java/lang/Object");
        System.out.println("  java/lang/String");
        System.out.println("  com/example/Main");
        System.out.println("  com/example/Service");
        System.out.println("  org/springframework/context/ApplicationContext");

        System.out.println("\n┌─────────────────────────────────────────┐");
        System.out.println("│ STEP 2: ARCHIVE CREATION                │");
        System.out.println("│ Build shared archive from class list    │");
        System.out.println("└─────────────────────────────────────────┘");

        System.out.println("\nCommand:");
        System.out.println("  java -Xshare:dump \\");
        System.out.println("       -XX:SharedClassListFile=classes.lst \\");
        System.out.println("       -XX:SharedArchiveFile=app.jsa \\");
        System.out.println("       -cp app.jar:lib/*");

        System.out.println("\nWhat happens:");
        System.out.println("  • JVM loads classes from list");
        System.out.println("  • Parses and verifies each class");
        System.out.println("  • Builds internal metadata");
        System.out.println("  • Writes to app.jsa file");
        System.out.println("  • Process takes 5-30 seconds");

        System.out.println("\nOutput:");
        System.out.println("  Number of classes 1234");
        System.out.println("  Allocated temp space: 42 MB");
        System.out.println("  Allocated shared space: 35 MB");
        System.out.println("    RO space:  15 MB");
        System.out.println("    RW space:  12 MB");
        System.out.println("    MD space:   8 MB");

        System.out.println("\n┌─────────────────────────────────────────┐");
        System.out.println("│ STEP 3: USE ARCHIVE                     │");
        System.out.println("│ Run application with shared archive     │");
        System.out.println("└─────────────────────────────────────────┘");

        System.out.println("\nCommand:");
        System.out.println("  java -Xshare:on \\");
        System.out.println("       -XX:SharedArchiveFile=app.jsa \\");
        System.out.println("       -cp app.jar:lib/* \\");
        System.out.println("       com.example.Main");

        System.out.println("\nWhat happens:");
        System.out.println("  • JVM memory-maps app.jsa");
        System.out.println("  • Classes loaded instantly from archive");
        System.out.println("  • No parsing or verification");
        System.out.println("  • Faster startup, shared memory");
    }

    public static void printTrainingRunBestPractices() {
        System.out.println("\n=== TRAINING RUN BEST PRACTICES ===");

        System.out.println("\n--- COVERAGE STRATEGIES ---");

        System.out.println("\n1. Representative Workload:");
        System.out.println("   Run typical application scenario");
        System.out.println("   • Process sample requests");
        System.out.println("   • Exercise common code paths");
        System.out.println("   • Include initialization");

        System.out.println("\n2. Comprehensive Coverage:");
        System.out.println("   Execute all features");
        System.out.println("   • All REST endpoints");
        System.out.println("   • All database operations");
        System.out.println("   • All service interactions");

        System.out.println("\n3. Multi-Scenario Approach:");
        System.out.println("   Combine multiple runs");
        System.out.println("   • Run scenario A → classes1.lst");
        System.out.println("   • Run scenario B → classes2.lst");
        System.out.println("   • Merge files → combined.lst");

        System.out.println("\n--- AUTOMATED TRAINING ---");

        System.out.println("\nUsing integration tests:");
        System.out.println("  #!/bin/bash");
        System.out.println("  ");
        System.out.println("  # Run integration tests with class tracking");
        System.out.println("  java -Xshare:off \\");
        System.out.println("       -XX:DumpLoadedClassList=classes.lst \\");
        System.out.println("       -cp app.jar:lib/*:test-lib/* \\");
        System.out.println("       org.junit.runner.JUnitCore \\");
        System.out.println("       com.example.IntegrationTests");

        System.out.println("\n--- FILTERING CLASS LIST ---");

        System.out.println("\nRemove test-only classes:");
        System.out.println("  grep -v 'org/junit' classes.lst > filtered.lst");
        System.out.println("  grep -v 'org/mockito' filtered.lst > final.lst");

        System.out.println("\nRemove rarely-used classes:");
        System.out.println("  # Keep only classes loaded by >50% of runs");
        System.out.println("  ./analyze-class-frequency.sh");
    }
}

Dynamic CDS (JDK 13+)

// Dynamic CDS
public class DynamicCDS {

    public static void printDynamicCDS() {
        System.out.println("=== DYNAMIC CDS (JDK 13+) ===");

        System.out.println("\n--- WHAT IS DYNAMIC CDS? ---");

        System.out.println("\nSimplified workflow:");
        System.out.println("  • No class list generation");
        System.out.println("  • Archive created automatically on exit");
        System.out.println("  • Single step instead of three");

        System.out.println("\n--- USAGE ---");

        System.out.println("\n┌─────────────────────────────────────────┐");
        System.out.println("│ STEP 1: CREATE ARCHIVE ON EXIT          │");
        System.out.println("└─────────────────────────────────────────┘");

        System.out.println("\nCommand:");
        System.out.println("  java -XX:ArchiveClassesAtExit=app-dynamic.jsa \\");
        System.out.println("       -cp app.jar:lib/* \\");
        System.out.println("       com.example.Main");

        System.out.println("\nWhat happens:");
        System.out.println("  • Application runs normally");
        System.out.println("  • JVM tracks loaded classes");
        System.out.println("  • On clean exit (Ctrl+C or normal shutdown):");
        System.out.println("    → Creates app-dynamic.jsa");
        System.out.println("    → Includes all loaded classes");

        System.out.println("\n┌─────────────────────────────────────────┐");
        System.out.println("│ STEP 2: USE ARCHIVE                     │");
        System.out.println("└─────────────────────────────────────────┘");

        System.out.println("\nCommand:");
        System.out.println("  java -XX:SharedArchiveFile=app-dynamic.jsa \\");
        System.out.println("       -cp app.jar:lib/* \\");
        System.out.println("       com.example.Main");

        System.out.println("\n--- DYNAMIC VS STATIC ---");

        System.out.println("\nDynamic CDS Advantages:");
        System.out.println("  ✓ Simpler workflow (1 step vs 3)");
        System.out.println("  ✓ No class list management");
        System.out.println("  ✓ Automatic coverage");
        System.out.println("  ✓ Easy to regenerate");

        System.out.println("\nStatic CDS Advantages:");
        System.out.println("  ✓ More control over contents");
        System.out.println("  ✓ Can filter classes");
        System.out.println("  ✓ Can combine multiple runs");
        System.out.println("  ✓ Reproducible builds");

        System.out.println("\n--- USE CASES ---");

        System.out.println("\nUse Dynamic CDS when:");
        System.out.println("  • Quick setup desired");
        System.out.println("  • Simple applications");
        System.out.println("  • Development/testing");
        System.out.println("  • Frequent archive regeneration");

        System.out.println("\nUse Static CDS when:");
        System.out.println("  • Need precise control");
        System.out.println("  • CI/CD integration");
        System.out.println("  • Production deployments");
        System.out.println("  • Multiple coverage scenarios");
    }

    public static void printDynamicCDSExamples() {
        System.out.println("\n=== DYNAMIC CDS EXAMPLES ===");

        System.out.println("\n--- WEB APPLICATION ---");

        System.out.println("\nSpring Boot application:");
        System.out.println("  # Start app and create archive on exit");
        System.out.println("  java -XX:ArchiveClassesAtExit=springboot.jsa \\");
        System.out.println("       -jar myapp.jar");
        System.out.println("  ");
        System.out.println("  # Let it initialize fully");
        System.out.println("  # Make some test requests");
        System.out.println("  # Ctrl+C to stop → archive created");
        System.out.println("  ");
        System.out.println("  # Subsequent runs use archive");
        System.out.println("  java -XX:SharedArchiveFile=springboot.jsa \\");
        System.out.println("       -jar myapp.jar");

        System.out.println("\n--- MICROSERVICE IN KUBERNETES ---");

        System.out.println("\nDockerfile:");
        System.out.println("  FROM openjdk:17");
        System.out.println("  COPY app.jar /app/");
        System.out.println("  WORKDIR /app");
        System.out.println("  ");
        System.out.println("  # Create archive during image build");
        System.out.println("  RUN java -XX:ArchiveClassesAtExit=app.jsa \\");
        System.out.println("           -jar app.jar --warmup-mode &");
        System.out.println("  RUN sleep 30 && pkill java  # Let warmup complete");
        System.out.println("  ");
        System.out.println("  # Runtime uses archive");
        System.out.println("  CMD [\"java\", \"-XX:SharedArchiveFile=app.jsa\", \\");
        System.out.println("       \"-jar\", \"app.jar\"]");

        System.out.println("\n--- BATCH JOB ---");

        System.out.println("\nOne-time setup:");
        System.out.println("  java -XX:ArchiveClassesAtExit=batch.jsa \\");
        System.out.println("       -cp app.jar:lib/* \\");
        System.out.println("       com.example.BatchJob --sample-run");

        System.out.println("\nDaily execution (100 times):");
        System.out.println("  for i in {1..100}; do");
        System.out.println("    java -XX:SharedArchiveFile=batch.jsa \\");
        System.out.println("         -cp app.jar:lib/* \\");
        System.out.println("         com.example.BatchJob --process-data");
        System.out.println("  done");
        System.out.println("  # Each run ~40% faster startup");
    }
}

Layered Archives (JDK 17+)

// Layered CDS Archives
public class LayeredArchives {

    public static void printLayeredArchives() {
        System.out.println("=== LAYERED CDS ARCHIVES (JDK 17+) ===");

        System.out.println("\n--- CONCEPT ---");

        System.out.println("\nArchive layers:");
        System.out.println("  ┌────────────────────┐");
        System.out.println("  │  Application       │ ← Top layer");
        System.out.println("  │  (app-specific)    │");
        System.out.println("  ├────────────────────┤");
        System.out.println("  │  Framework         │ ← Middle layer");
        System.out.println("  │  (Spring, etc.)    │");
        System.out.println("  ├────────────────────┤");
        System.out.println("  │  JDK Classes       │ ← Base layer");
        System.out.println("  │  (default archive) │");
        System.out.println("  └────────────────────┘");

        System.out.println("\n--- BENEFITS ---");

        System.out.println("\n1. Reusability:");
        System.out.println("   • Base layer: Shared across all apps");
        System.out.println("   • Framework layer: Shared across apps using same framework");
        System.out.println("   • App layer: Application-specific");

        System.out.println("\n2. Flexibility:");
        System.out.println("   • Update app without regenerating framework layer");
        System.out.println("   • Share framework layer across microservices");

        System.out.println("\n3. Efficiency:");
        System.out.println("   • Smaller top-layer archives");
        System.out.println("   • Faster archive creation");
        System.out.println("   • Better memory sharing");

        System.out.println("\n--- CREATING LAYERS ---");

        System.out.println("\nLayer 1 - Framework:");
        System.out.println("  # Generate class list for framework");
        System.out.println("  java -Xshare:off \\");
        System.out.println("       -XX:DumpLoadedClassList=framework.lst \\");
        System.out.println("       -cp lib/spring-*.jar \\");
        System.out.println("       com.example.FrameworkInit");

        System.out.println("\n  # Create framework archive");
        System.out.println("  java -Xshare:dump \\");
        System.out.println("       -XX:SharedClassListFile=framework.lst \\");
        System.out.println("       -XX:SharedArchiveFile=framework.jsa \\");
        System.out.println("       -cp lib/*");

        System.out.println("\nLayer 2 - Application:");
        System.out.println("  # Generate class list using framework archive");
        System.out.println("  java -XX:SharedArchiveFile=framework.jsa \\");
        System.out.println("       -Xshare:on \\");
        System.out.println("       -XX:DumpLoadedClassList=app.lst \\");
        System.out.println("       -cp app.jar:lib/* \\");
        System.out.println("       com.example.Main");

        System.out.println("\n  # Create layered application archive");
        System.out.println("  java -XX:SharedArchiveFile=framework.jsa \\");
        System.out.println("       -XX:ArchiveClassesAtExit=app-layered.jsa \\");
        System.out.println("       -cp app.jar:lib/* \\");
        System.out.println("       com.example.Main");

        System.out.println("\n--- USING LAYERED ARCHIVES ---");

        System.out.println("\nWith two layers:");
        System.out.println("  java -XX:SharedArchiveFile=framework.jsa:app-layered.jsa \\");
        System.out.println("       -cp app.jar:lib/* \\");
        System.out.println("       com.example.Main");

        System.out.println("\nWith three layers (including default):");
        System.out.println("  java -XX:SharedArchiveFile=");
        System.out.println("         default.jsa:framework.jsa:app.jsa \\");
        System.out.println("       -cp app.jar:lib/* \\");
        System.out.println("       com.example.Main");
    }
}

Archive Management

// Archive Management
public class ArchiveManagement {

    public static void printArchiveManagement() {
        System.out.println("=== ARCHIVE MANAGEMENT ===");

        System.out.println("\n--- ARCHIVE INSPECTION ---");

        System.out.println("\nList classes in archive:");
        System.out.println("  java -Xshare:dump \\");
        System.out.println("       -XX:SharedArchiveFile=app.jsa \\");
        System.out.println("       -Xlog:cds=debug");

        System.out.println("\nArchive statistics:");
        System.out.println("  java -Xlog:cds+map=info \\");
        System.out.println("       -XX:SharedArchiveFile=app.jsa \\");
        System.out.println("       -version");

        System.out.println("\nVerify archive:");
        System.out.println("  java -Xshare:on \\");
        System.out.println("       -XX:SharedArchiveFile=app.jsa \\");
        System.out.println("       -Xlog:cds \\");
        System.out.println("       -version");

        System.out.println("\n--- VERSIONING STRATEGY ---");

        System.out.println("\nNaming convention:");
        System.out.println("  app-${VERSION}-${JDK_VERSION}.jsa");
        System.out.println("  ");
        System.out.println("  Examples:");
        System.out.println("    app-1.0.0-jdk17.0.5.jsa");
        System.out.println("    app-1.0.1-jdk17.0.5.jsa");
        System.out.println("    app-1.0.1-jdk17.0.6.jsa");

        System.out.println("\n--- STORAGE LOCATION ---");

        System.out.println("\nLocal development:");
        System.out.println("  ./target/app.jsa");
        System.out.println("  Regenerate frequently");

        System.out.println("\nProduction:");
        System.out.println("  /opt/app/config/app.jsa");
        System.out.println("  Read-only location");
        System.out.println("  Versioned with application");

        System.out.println("\nContainer:");
        System.out.println("  /app/app.jsa");
        System.out.println("  Baked into image");
        System.out.println("  Regenerated on image build");

        System.out.println("\n--- REGENERATION TRIGGERS ---");

        System.out.println("\nMust regenerate when:");
        System.out.println("  ✗ JDK version changes");
        System.out.println("  ✗ Classpath changes");
        System.out.println("  ✗ Application code changes");
        System.out.println("  ✗ Library versions change");

        System.out.println("\nCan reuse when:");
        System.out.println("  ✓ Configuration files change");
        System.out.println("  ✓ Data files change");
        System.out.println("  ✓ Environment variables change");

        System.out.println("\n--- CI/CD INTEGRATION ---");

        System.out.println("\nGitLab CI example:");
        System.out.println("  build-cds-archive:");
        System.out.println("    stage: build");
        System.out.println("    script:");
        System.out.println("      - mvn package");
        System.out.println("      - java -XX:ArchiveClassesAtExit=app.jsa \\");
        System.out.println("             -jar target/app.jar --warmup");
        System.out.println("    artifacts:");
        System.out.println("      paths:");
        System.out.println("        - app.jsa");

        System.out.println("\nDocker multi-stage build:");
        System.out.println("  # Stage 1: Build archive");
        System.out.println("  FROM openjdk:17 AS builder");
        System.out.println("  COPY app.jar /tmp/");
        System.out.println("  RUN java -XX:ArchiveClassesAtExit=/tmp/app.jsa \\");
        System.out.println("           -jar /tmp/app.jar --create-archive");
        System.out.println("  ");
        System.out.println("  # Stage 2: Runtime image");
        System.out.println("  FROM openjdk:17-slim");
        System.out.println("  COPY --from=builder /tmp/app.jsa /app/");
        System.out.println("  COPY app.jar /app/");
        System.out.println("  CMD [\"java\", \"-XX:SharedArchiveFile=/app/app.jsa\", \\");
        System.out.println("       \"-jar\", \"/app/app.jar\"]");
    }
}

Best Practices

  • Use dynamic CDS for simplicity: JDK 13+ makes archive creation trivial.
  • Automate archive generation: Include in CI/CD pipeline.
  • Version archives properly: Tie to application and JDK version.
  • Test with -Xshare:on: Catch mismatches early in development.
  • Store in container images: Pre-generate during image build.
  • Use layered archives: Share framework layer across microservices.
  • Monitor archive usage: Use -Xlog:cds to verify loading.
  • Keep archives up-to-date: Regenerate on any dependency change.
  • Document warmup process: Ensure representative coverage.
  • Measure impact: Benchmark before/after to validate benefits.