27.4 Performance Analysis and Tuning

Effective JIT performance analysis requires understanding compilation behavior, identifying bottlenecks, and applying appropriate optimizations.

Compilation Logging and Analysis

// Compilation Monitoring
public class CompilationMonitoring {

    public static void printCompilationLogging() {
        System.out.println("=== COMPILATION LOGGING ===");

        System.out.println("\n--- PRINTCOMPILATION ---");
        System.out.println("Flag: -XX:+PrintCompilation");

        System.out.println("\nOutput format:");
        System.out.println("  timestamp compile_id attributes tier method_name size deopt");

        System.out.println("\nExample:");
        System.out.println("  100   1       3       java.lang.String::hashCode (55 bytes)");
        System.out.println("  150   2    !  4       com.example.MyClass::hotMethod (120 bytes)");
        System.out.println("  200   1       3  made not entrant  java.lang.String::hashCode");

        System.out.println("\n--- OUTPUT COMPONENTS ---");

        System.out.println("\nTimestamp: Milliseconds since JVM start");

        System.out.println("\nCompile ID: Unique compilation identifier");

        System.out.println("\nAttributes:");
        System.out.println("  %  On-stack replacement (OSR)");
        System.out.println("  s  Synchronized method");
        System.out.println("  !  Has exception handlers");
        System.out.println("  b  Blocking compilation");
        System.out.println("  n  Native method wrapper");

        System.out.println("\nTier:");
        System.out.println("  1  C1 without profiling");
        System.out.println("  2  C1 with limited profiling");
        System.out.println("  3  C1 with full profiling");
        System.out.println("  4  C2 (server compiler)");

        System.out.println("\nStatus:");
        System.out.println("  made not entrant: Invalidated, won't be used for new calls");
        System.out.println("  made zombie: Can be reclaimed from code cache");

        System.out.println("\n--- LOG COMPILATION ---");
        System.out.println("Flag: -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation");
        System.out.println("Output: hotspot_pid12345.log (XML format)");

        System.out.println("\nContains:");
        System.out.println("  • Detailed compilation information");
        System.out.println("  • Inlining decisions");
        System.out.println("  • Optimization details");
        System.out.println("  • Deoptimization reasons");

        System.out.println("\nAnalysis tools:");
        System.out.println("  • JITWatch: GUI for log analysis");
        System.out.println("  • Download: github.com/AdoptOpenJDK/jitwatch");
    }

    public static void printInliningAnalysis() {
        System.out.println("\n=== INLINING ANALYSIS ===");

        System.out.println("\nFlags:");
        System.out.println("  -XX:+UnlockDiagnosticVMOptions");
        System.out.println("  -XX:+PrintInlining");

        System.out.println("\nOutput format:");
        System.out.println("  @ byte_offset class::method (size) reason");

        System.out.println("\nExample:");
        System.out.println("  @ 10  MyClass::add (10 bytes)  inline");
        System.out.println("  @ 20  OtherClass::compute (200 bytes)  too big");
        System.out.println("  @ 30  Interface::method (50 bytes)  not inlineable");

        System.out.println("\n--- INLINING DECISIONS ---");

        System.out.println("\nSuccessful inlining:");
        System.out.println("  inline                   Method inlined");
        System.out.println("  inline (hot)            Hot method inlined");

        System.out.println("\nFailed inlining:");
        System.out.println("  too big                 Exceeds size threshold");
        System.out.println("  hot method too big      Hot but still too large");
        System.out.println("  already compiled        Target already compiled separately");
        System.out.println("  not inlineable          Virtual/interface with multiple targets");
        System.out.println("  no static binding       Cannot determine target");
        System.out.println("  callee is too large     Combined size exceeds limit");

        System.out.println("\n--- OPTIMIZATION OPPORTUNITIES ---");

        System.out.println("\nIf you see 'too big':");
        System.out.println("  • Consider refactoring large methods");
        System.out.println("  • Extract cold code to separate methods");
        System.out.println("  • Use -XX:MaxInlineSize=50 to increase limit");

        System.out.println("\nIf you see 'not inlineable':");
        System.out.println("  • Interface call with multiple implementations");
        System.out.println("  • Consider using concrete class in hot path");
        System.out.println("  • Profile may show monomorphic - will inline");
    }
}

Using JFR for Compilation Analysis

// JFR Compilation Events
public class JFRCompilationAnalysis {

    public static void printJFRCompilationEvents() {
        System.out.println("=== JFR COMPILATION EVENTS ===");

        System.out.println("\n--- STARTING JFR RECORDING ---");

        System.out.println("\nCommand line:");
        System.out.println("  java -XX:StartFlightRecording=");
        System.out.println("       settings=profile,");
        System.out.println("       filename=recording.jfr,");
        System.out.println("       duration=60s");
        System.out.println("       MyApp");

        System.out.println("\nOr with jcmd:");
        System.out.println("  jcmd <pid> JFR.start");
        System.out.println("             duration=60s");
        System.out.println("             filename=recording.jfr");

        System.out.println("\n--- KEY COMPILATION EVENTS ---");

        System.out.println("\n1. jdk.Compilation");
        System.out.println("   Information:");
        System.out.println("     • Method name");
        System.out.println("     • Compilation tier");
        System.out.println("     • Duration");
        System.out.println("     • Bytecode size");
        System.out.println("     • Compiled code size");

        System.out.println("\n2. jdk.CompilationFailure");
        System.out.println("   When compilation fails");
        System.out.println("     • Method name");
        System.out.println("     • Failure reason");

        System.out.println("\n3. jdk.Deoptimization");
        System.out.println("   When optimized code invalidated");
        System.out.println("     • Method name");
        System.out.println("     • Reason (class_check, null_check, etc.)");
        System.out.println("     • Location");

        System.out.println("\n4. jdk.CodeCacheFull");
        System.out.println("   When code cache exhausted");
        System.out.println("     • Segment (profiled/non-profiled)");
        System.out.println("     • Size");

        System.out.println("\n5. jdk.CodeCacheStatistics");
        System.out.println("   Periodic code cache usage");
        System.out.println("     • Used");
        System.out.println("     • Free");
        System.out.println("     • Total");

        System.out.println("\n--- ANALYZING WITH JMC ---");

        System.out.println("\nJava Mission Control (JMC)");
        System.out.println("  Download: jdk.java.net/jmc");

        System.out.println("\nAnalysis steps:");
        System.out.println("  1. Open recording.jfr in JMC");
        System.out.println("  2. Navigate to Code tab");
        System.out.println("  3. View compilation timeline");
        System.out.println("  4. Identify hot methods");
        System.out.println("  5. Check deoptimizations");
        System.out.println("  6. Analyze compilation failures");

        System.out.println("\nKey insights:");
        System.out.println("  ✓ Which methods are hot");
        System.out.println("  ✓ Compilation bottlenecks");
        System.out.println("  ✓ Deoptimization patterns");
        System.out.println("  ✓ Code cache utilization");
    }
}

Performance Benchmarking with JMH

// JMH Benchmarking
public class JMHBenchmarking {

    public static void printJMHUsage() {
        System.out.println("=== JAVA MICROBENCHMARK HARNESS (JMH) ===");

        System.out.println("\n--- WHY JMH? ---");
        System.out.println("✓ Proper warmup handling");
        System.out.println("✓ Dead code elimination detection");
        System.out.println("✓ Constant folding detection");
        System.out.println("✓ Statistical analysis");
        System.out.println("✓ Fork control (JVM isolation)");
        System.out.println("✓ Prevents common benchmarking mistakes");

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

        System.out.println("\nMaven dependency:");
        System.out.println("  <dependency>");
        System.out.println("    <groupId>org.openjdk.jmh</groupId>");
        System.out.println("    <artifactId>jmh-core</artifactId>");
        System.out.println("    <version>1.37</version>");
        System.out.println("  </dependency>");

        System.out.println("\n--- BASIC BENCHMARK ---");

        System.out.println("\nimport org.openjdk.jmh.annotations.*;");
        System.out.println("import java.util.concurrent.TimeUnit;");
        System.out.println();
        System.out.println("@BenchmarkMode(Mode.AverageTime)");
        System.out.println("@OutputTimeUnit(TimeUnit.NANOSECONDS)");
        System.out.println("@Warmup(iterations = 5, time = 1)");
        System.out.println("@Measurement(iterations = 5, time = 1)");
        System.out.println("@Fork(1)");
        System.out.println("@State(Scope.Benchmark)");
        System.out.println("public class MyBenchmark {");
        System.out.println();
        System.out.println("    private int[] data = new int[1000];");
        System.out.println();
        System.out.println("    @Setup");
        System.out.println("    public void setup() {");
        System.out.println("        for (int i = 0; i < data.length; i++) {");
        System.out.println("            data[i] = i;");
        System.out.println("        }");
        System.out.println("    }");
        System.out.println();
        System.out.println("    @Benchmark");
        System.out.println("    public long sumArray() {");
        System.out.println("        long sum = 0;");
        System.out.println("        for (int value : data) {");
        System.out.println("            sum += value;");
        System.out.println("        }");
        System.out.println("        return sum;");
        System.out.println("    }");
        System.out.println("}");

        System.out.println("\n--- BENCHMARK MODES ---");

        System.out.println("\nMode.Throughput:");
        System.out.println("  Operations per second");
        System.out.println("  Higher is better");

        System.out.println("\nMode.AverageTime:");
        System.out.println("  Average time per operation");
        System.out.println("  Lower is better");

        System.out.println("\nMode.SampleTime:");
        System.out.println("  Sample time distribution");
        System.out.println("  Shows percentiles");

        System.out.println("\nMode.SingleShotTime:");
        System.out.println("  Single execution time");
        System.out.println("  For cold-start testing");

        System.out.println("\n--- PREVENTING DEAD CODE ELIMINATION ---");

        System.out.println("\n❌ Wrong:");
        System.out.println("  @Benchmark");
        System.out.println("  public void compute() {");
        System.out.println("    int result = expensiveComputation();");
        System.out.println("    // Result unused - JIT may eliminate!");
        System.out.println("  }");

        System.out.println("\n✓ Correct:");
        System.out.println("  @Benchmark");
        System.out.println("  public int compute() {");
        System.out.println("    return expensiveComputation();");
        System.out.println("    // Force result to be used");
        System.out.println("  }");

        System.out.println("\nOr use Blackhole:");
        System.out.println("  @Benchmark");
        System.out.println("  public void compute(Blackhole bh) {");
        System.out.println("    bh.consume(expensiveComputation());");
        System.out.println("  }");

        System.out.println("\n--- PROFILING WITH JMH ---");

        System.out.println("\nAdd profilers:");
        System.out.println("  @Fork(value = 1, jvmArgs = {");
        System.out.println("    \"-XX:+UnlockDiagnosticVMOptions\",");
        System.out.println("    \"-XX:+PrintCompilation\"");
        System.out.println("  })");

        System.out.println("\nBuilt-in profilers:");
        System.out.println("  -prof gc         GC profiling");
        System.out.println("  -prof stack      Stack profiling");
        System.out.println("  -prof perfasm    Assembly-level profiling");
        System.out.println("  -prof perfnorm   Performance counters");
    }
}

Common Performance Issues

// Common JIT Performance Issues
public class CommonPerformanceIssues {

    public static void printCommonIssues() {
        System.out.println("=== COMMON JIT PERFORMANCE ISSUES ===");

        System.out.println("\n--- 1. MEGAMORPHIC CALL SITES ---");

        System.out.println("\nProblem:");
        System.out.println("  Interface with many implementations in hot loop");

        System.out.println("\nExample:");
        System.out.println("  interface Handler {");
        System.out.println("    void handle(Event event);");
        System.out.println("  }");
        System.out.println("  ");
        System.out.println("  // Many implementations: Handler1, Handler2, ..., Handler10");
        System.out.println("  for (Handler h : handlers) {");
        System.out.println("    h.handle(event);  // Megamorphic!");
        System.out.println("  }");

        System.out.println("\nImpact:");
        System.out.println("  ✗ No inlining");
        System.out.println("  ✗ Virtual call overhead");
        System.out.println("  ✗ Poor CPU branch prediction");

        System.out.println("\nSolution:");
        System.out.println("  • Reduce number of implementations");
        System.out.println("  • Use visitor pattern");
        System.out.println("  • Type-specific loops");
        System.out.println("  • Batch by type");

        System.out.println("\n--- 2. CODE CACHE EXHAUSTION ---");

        System.out.println("\nSymptoms:");
        System.out.println("  • Log message: 'CodeCache is full'");
        System.out.println("  • Compilation stops");
        System.out.println("  • Performance degrades");

        System.out.println("\nCauses:");
        System.out.println("  • Too many compiled methods");
        System.out.println("  • Large methods");
        System.out.println("  • Aggressive inlining");

        System.out.println("\nSolution:");
        System.out.println("  -XX:ReservedCodeCacheSize=512m");
        System.out.println("  (Increase from default ~240MB)");

        System.out.println("\n--- 3. EXCESSIVE DEOPTIMIZATION ---");

        System.out.println("\nSymptoms:");
        System.out.println("  • Frequent 'made not entrant' in PrintCompilation");
        System.out.println("  • Performance fluctuations");

        System.out.println("\nCauses:");
        System.out.println("  • Profile pollution");
        System.out.println("  • Workload changes");
        System.out.println("  • Class loading during warmup");

        System.out.println("\nSolution:");
        System.out.println("  • Better warmup with representative data");
        System.out.println("  • Preload classes");
        System.out.println("  • Separate init from steady state");

        System.out.println("\n--- 4. LARGE METHODS ---");

        System.out.println("\nProblem:");
        System.out.println("  Methods >8KB bytecode");

        System.out.println("\nImpact:");
        System.out.println("  ✗ Won't be compiled");
        System.out.println("  ✗ Won't be inlined");
        System.out.println("  ✗ Stays interpreted");

        System.out.println("\nSolution:");
        System.out.println("  • Refactor into smaller methods");
        System.out.println("  • Extract cold code");
        System.out.println("  • Use -XX:HugeMethodLimit=10000 (last resort)");

        System.out.println("\n--- 5. REFLECTION IN HOT PATH ---");

        System.out.println("\nProblem:");
        System.out.println("  Method.invoke() in tight loop");

        System.out.println("\nImpact:");
        System.out.println("  ✗ Cannot inline");
        System.out.println("  ✗ Slow invocation");
        System.out.println("  ✗ Boxing overhead");

        System.out.println("\nSolution:");
        System.out.println("  • Use MethodHandles instead");
        System.out.println("  • Generate code with asm/javassist");
        System.out.println("  • Cache and reuse");

        System.out.println("\n--- 6. BOXING IN LOOPS ---");

        System.out.println("\nProblem:");
        System.out.println("  for (Integer i : list) {  // Unboxing");
        System.out.println("    sum += i;  // Boxing/unboxing");
        System.out.println("  }");

        System.out.println("\nImpact:");
        System.out.println("  ✗ Object allocation");
        System.out.println("  ✗ GC pressure");
        System.out.println("  ✗ Cache misses");

        System.out.println("\nSolution:");
        System.out.println("  • Use primitive arrays");
        System.out.println("  • IntStream instead of Stream<Integer>");
        System.out.println("  • Specialized collections (Trove, FastUtil)");
    }
}

Compilation Tuning Strategies

// JIT Tuning Strategies
public class CompilationTuning {

    public static void printTuningStrategies() {
        System.out.println("=== COMPILATION TUNING STRATEGIES ===");

        System.out.println("\n--- WHEN TO TUNE ---");

        System.out.println("\n✓ Tune if:");
        System.out.println("  • Profiling shows compilation bottleneck");
        System.out.println("  • Code cache exhausted");
        System.out.println("  • Specific methods not compiled");
        System.out.println("  • Excessive deoptimizations");

        System.out.println("\n✗ Don't tune if:");
        System.out.println("  • Just starting optimization");
        System.out.println("  • No evidence of JIT issues");
        System.out.println("  • Algorithm or I/O is bottleneck");

        System.out.println("\n--- CONSERVATIVE TUNING ---");

        System.out.println("\n1. Code cache size:");
        System.out.println("   -XX:ReservedCodeCacheSize=512m");
        System.out.println("   Safe increase for large applications");

        System.out.println("\n2. Inlining limits:");
        System.out.println("   -XX:MaxInlineSize=50");
        System.out.println("   -XX:FreqInlineSize=400");
        System.out.println("   Slightly more aggressive inlining");

        System.out.println("\n3. Print compilation:");
        System.out.println("   -XX:+PrintCompilation");
        System.out.println("   Monitor compilation activity");

        System.out.println("\n--- AGGRESSIVE TUNING ---");

        System.out.println("\n⚠ Only after profiling proves benefit");

        System.out.println("\n1. Lower compilation thresholds:");
        System.out.println("   -XX:CompileThreshold=1000");
        System.out.println("   Faster warmup, less profiling");

        System.out.println("\n2. More aggressive inlining:");
        System.out.println("   -XX:MaxInlineLevel=12");
        System.out.println("   -XX:InlineSmallCode=3000");
        System.out.println("   Deeper inlining");

        System.out.println("\n3. Disable C1 (C2 only):");
        System.out.println("   -XX:-TieredCompilation");
        System.out.println("   Skip warmup tier");

        System.out.println("\n--- DIAGNOSTIC FLAGS ---");

        System.out.println("\nCompilation analysis:");
        System.out.println("  -XX:+PrintCompilation");
        System.out.println("  -XX:+UnlockDiagnosticVMOptions");
        System.out.println("  -XX:+PrintInlining");
        System.out.println("  -XX:+LogCompilation");

        System.out.println("\nCode cache monitoring:");
        System.out.println("  -XX:+PrintCodeCache");

        System.out.println("\nDeoptimization tracking:");
        System.out.println("  -XX:+UnlockDiagnosticVMOptions");
        System.out.println("  -XX:+LogCompilation");

        System.out.println("\n--- METHOD-SPECIFIC TUNING ---");

        System.out.println("\nExclude from compilation:");
        System.out.println("  -XX:CompileCommand=exclude,");
        System.out.println("      com/example/MyClass::initMethod");

        System.out.println("\nForce compilation:");
        System.out.println("  -XX:CompileCommand=compileonly,");
        System.out.println("      com/example/MyClass::hotMethod");

        System.out.println("\nInline specific method:");
        System.out.println("  -XX:CompileCommand=inline,");
        System.out.println("      com/example/Helper::*");

        System.out.println("\n--- BENCHMARKING WORKFLOW ---");

        System.out.println("\n1. Establish baseline");
        System.out.println("   • Run with default settings");
        System.out.println("   • Measure with JMH");
        System.out.println("   • Record compilation logs");

        System.out.println("\n2. Identify bottlenecks");
        System.out.println("   • Analyze JFR recording");
        System.out.println("   • Check PrintCompilation output");
        System.out.println("   • Look for deoptimizations");

        System.out.println("\n3. Apply targeted tuning");
        System.out.println("   • Change one parameter");
        System.out.println("   • Measure impact");
        System.out.println("   • Keep if beneficial");

        System.out.println("\n4. Validate in production");
        System.out.println("   • Test under real load");
        System.out.println("   • Monitor metrics");
        System.out.println("   • Roll back if issues");
    }
}

Performance Checklist

// JIT Performance Checklist
public class PerformanceChecklist {

    public static void printChecklist() {
        System.out.println("=== JIT PERFORMANCE CHECKLIST ===");

        System.out.println("\n--- BASELINE SETUP ---");
        System.out.println("☐ Enable tiered compilation (default)");
        System.out.println("☐ Set adequate code cache size");
        System.out.println("☐ Use production-like JVM flags");
        System.out.println("☐ Ensure proper warmup time");

        System.out.println("\n--- MONITORING ---");
        System.out.println("☐ Enable -XX:+PrintCompilation");
        System.out.println("☐ Monitor code cache usage");
        System.out.println("☐ Track deoptimizations");
        System.out.println("☐ Use JFR for profiling");

        System.out.println("\n--- CODE QUALITY ---");
        System.out.println("☐ Keep hot methods small (<325 bytes)");
        System.out.println("☐ Limit polymorphism in hot paths");
        System.out.println("☐ Use final for constants");
        System.out.println("☐ Avoid reflection in tight loops");
        System.out.println("☐ Use primitives over boxed types");
        System.out.println("☐ Leverage library intrinsics");

        System.out.println("\n--- BENCHMARKING ---");
        System.out.println("☐ Use JMH for micro-benchmarks");
        System.out.println("☐ Run adequate warmup iterations");
        System.out.println("☐ Test with realistic data");
        System.out.println("☐ Measure in production environment");
        System.out.println("☐ Account for GC impact");

        System.out.println("\n--- ANALYSIS ---");
        System.out.println("☐ Check which methods are hot (JFR)");
        System.out.println("☐ Verify hot methods are compiled (Tier 4)");
        System.out.println("☐ Review inlining decisions");
        System.out.println("☐ Identify megamorphic call sites");
        System.out.println("☐ Look for excessive deoptimizations");

        System.out.println("\n--- TUNING (IF NEEDED) ---");
        System.out.println("☐ Increase code cache if exhausted");
        System.out.println("☐ Adjust inlining limits cautiously");
        System.out.println("☐ Consider method-specific compilation commands");
        System.out.println("☐ Validate improvements with benchmarks");

        System.out.println("\n--- VALIDATION ---");
        System.out.println("☐ Test under production load");
        System.out.println("☐ Monitor metrics in staging");
        System.out.println("☐ Gradual rollout in production");
        System.out.println("☐ Have rollback plan ready");
    }
}

Best Practices

  • Profile before tuning: Use JFR and JMH to identify actual bottlenecks.
  • Enable compilation logging: Monitor JIT behavior in development.
  • Use JMH for benchmarks: Avoid common microbenchmarking pitfalls.
  • Start with defaults: JVM's default settings are well-tuned.
  • Tune conservatively: Small, incremental changes with validation.
  • Monitor code cache: Ensure adequate space for compiled code.
  • Keep methods small: Easier for JIT to inline and optimize.
  • Validate in production: Synthetic benchmarks don't capture real behavior.
  • Document tuning decisions: Record why each flag was added.
  • Use JITWatch for analysis: Visual tool for compilation log analysis.