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.