On-Stack Replacement (OSR) in Java: Deep Dive into Dynamic Code Optimization

On-Stack Replacement (OSR) is a crucial JVM optimization technique that allows replacing currently executing code with optimized versions while the code is running. This article provides a comprehensive exploration of OSR in Java, including implementation details, use cases, and practical examples.

Understanding OSR Fundamentals

Step 1: Core Concepts and Problem Statement

package com.example.osr;
public class OSRCoreConcepts {
// Problem: Long-running methods may never get optimized
public static long sumPrimitiveLoop(int n) {
long sum = 0;
for (int i = 0; i < n; i++) {
sum += i;
// Without OSR, this loop would run in interpreted mode
// until the method completes and becomes "hot"
}
return sum;
}
// Hot loop that benefits from OSR
public static void processLargeArray(int[] data, int threshold) {
int operations = 0;
for (int i = 0; i < data.length; i++) {
// This loop body becomes hot quickly
if (data[i] > threshold) {
data[i] = transformValue(data[i]);
operations++;
}
// Without OSR, we'd wait for method completion to optimize
// With OSR, the loop can be optimized while running
}
System.out.println("Operations performed: " + operations);
}
private static int transformValue(int value) {
return value * 2 + 1;
}
// Method that demonstrates OSR triggers
public static void demonstrateOSRScenario() {
// Phase 1: Interpretation
System.out.println("Starting in interpreted mode...");
// Phase 2: OSR kicks in for hot loop
long result = 0;
for (int i = 0; i < 1000000; i++) {
result += complexCalculation(i);
// After certain iterations, JVM detects this as hot code
// and performs OSR to replace with compiled version
}
System.out.println("Result: " + result);
}
private static long complexCalculation(int n) {
// Simulate some computation
return (long) n * n + 2 * n + 1;
}
}

JVM Internals and OSR Mechanics

Step 2: Understanding JVM OSR Implementation

package com.example.osr.jvm;
// Simplified representation of JVM OSR mechanics
public class OSRMechanics {
public static class MethodState {
private final String methodName;
private int invocationCount;
private int backedgeCount; // Loop back-edge counter
private boolean compiled;
private CompilationLevel compilationLevel;
public MethodState(String methodName) {
this.methodName = methodName;
this.invocationCount = 0;
this.backedgeCount = 0;
this.compiled = false;
this.compilationLevel = CompilationLevel.INTERPRETED;
}
public void recordInvocation() {
invocationCount++;
checkForCompilation();
}
public void recordBackedge() {
backedgeCount++;
checkForOSR();
}
private void checkForCompilation() {
// Tiered compilation thresholds
if (!compiled && invocationCount >= 1000) { // C1 threshold
triggerCompilation(CompilationLevel.SIMPLE);
}
if (invocationCount >= 10000) { // C2 threshold
triggerCompilation(CompilationLevel.FULL);
}
}
private void checkForOSR() {
// OSR threshold based on backedges
if (!compiled && backedgeCount >= 10000) {
triggerOSRCompilation();
}
}
private void triggerCompilation(CompilationLevel level) {
System.out.println("Compiling method: " + methodName + " at level: " + level);
this.compilationLevel = level;
this.compiled = true;
}
private void triggerOSRCompilation() {
System.out.println("Triggering OSR for method: " + methodName);
this.compilationLevel = CompilationLevel.OSR;
this.compiled = true;
}
// Getters
public boolean isCompiled() { return compiled; }
public CompilationLevel getCompilationLevel() { return compilationLevel; }
}
public enum CompilationLevel {
INTERPRETED,    // Pure interpretation
OSR,           // On-Stack Replacement compiled
SIMPLE,        // C1 compiled
FULL           // C2 compiled
}
// Frame representation for OSR
public static class StackFrame {
private final String methodName;
private final int bci; // Bytecode index
private final Object[] locals;
private final Object[] stack;
private final Object lockObject; // For synchronized methods
public StackFrame(String methodName, int bci, Object[] locals, 
Object[] stack, Object lockObject) {
this.methodName = methodName;
this.bci = bci;
this.locals = locals;
this.stack = stack;
this.lockObject = lockObject;
}
// OSR transition point
public OSRTransition prepareOSRTransition() {
return new OSRTransition(this);
}
// Getters
public String getMethodName() { return methodName; }
public int getBci() { return bci; }
public Object[] getLocals() { return locals; }
public Object[] getStack() { return stack; }
}
public static class OSRTransition {
private final StackFrame oldFrame;
private StackFrame newFrame;
private boolean transitionReady;
public OSRTransition(StackFrame oldFrame) {
this.oldFrame = oldFrame;
this.transitionReady = false;
}
public void prepareCompiledFrame(MethodState newMethodState) {
// Convert interpreted frame to compiled frame format
this.newFrame = transformFrameForCompiledCode(oldFrame);
this.transitionReady = true;
}
public void executeTransition() {
if (transitionReady && newFrame != null) {
System.out.println("Executing OSR transition from interpreted to compiled code");
// Actual transition happens here in JVM
swapFrames(oldFrame, newFrame);
}
}
private StackFrame transformFrameForCompiledCode(StackFrame interpretedFrame) {
// Convert interpreted frame layout to compiled code layout
// This involves:
// 1. Mapping local variables
// 2. Converting stack values
// 3. Handling monitor state
return new StackFrame(
interpretedFrame.getMethodName(),
interpretedFrame.getBci(),
interpretedFrame.getLocals(),
interpretedFrame.getStack(),
interpretedFrame.lockObject
);
}
private void swapFrames(StackFrame oldFrame, StackFrame newFrame) {
// In actual JVM, this would involve:
// 1. Patching return address
// 2. Updating frame pointers
// 3. Transferring state
System.out.println("Frame swapped successfully");
}
}
}

OSR in HotSpot JVM

Step 3: HotSpot-specific OSR Details

package com.example.osr.hotspot;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.List;
public class HotSpotOSR {
// JVM flags related to OSR
public static class OSRFlags {
public static final String COMPILE_THRESHOLD = "CompileThreshold";
public static final String TIERED_COMPILATION = "TieredCompilation";
public static final String OSR_COMPILATION = "OnStackReplace";
public static final String BACKEDGE_THRESHOLD = "BackEdgeThreshold";
public static void printOSRRelatedFlags() {
RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
List<String> arguments = runtimeMxBean.getInputArguments();
System.out.println("OSR-related JVM flags:");
for (String arg : arguments) {
if (arg.contains("Compile") || 
arg.contains("OSR") || 
arg.contains("BackEdge") ||
arg.contains("Tiered")) {
System.out.println("  " + arg);
}
}
}
}
// Methods that demonstrate OSR behavior
public static class OSRDemonstrator {
// This method will trigger OSR due to long-running loop
public static long hotLoopOSR(int iterations) {
long result = 0;
// Phase 1: Interpreted execution
System.out.println("Starting loop in interpreted mode...");
for (int i = 0; i < iterations; i++) {
result += compute(i);
// OSR trigger point - after many backedges
if (i == 10000) {
System.out.println("OSR likely triggered around iteration 10000");
}
}
return result;
}
// Nested loops - multiple OSR opportunities
public static void nestedLoopOSR(int outer, int inner) {
int total = 0;
for (int i = 0; i < outer; i++) {
// Outer loop might get OSR
for (int j = 0; j < inner; j++) {
// Inner loop is more likely to get OSR first
total += i * j;
// Complex computation to make loop "hot"
if (total % 1000 == 0) {
total = manipulate(total);
}
}
}
System.out.println("Nested loop result: " + total);
}
private static int manipulate(int value) {
return (value * 31) ^ 0xDEADBEEF;
}
private static long compute(int n) {
// Make computation non-trivial
return (long) Math.sqrt(n) * n + n % 7;
}
// Method with multiple hot spots
public static void multipleHotSpots(int size) {
int[] data = new int[size];
// First hot spot
for (int i = 0; i < size; i++) {
data[i] = i * 2;
}
// Second hot spot
long sum = 0;
for (int i = 0; i < size; i++) {
if (data[i] > 1000) {
sum += data[i];
}
}
// Third hot spot
for (int i = 0; i < size; i++) {
data[i] = transform(data[i]);
}
System.out.println("Multiple hot spots processed, sum: " + sum);
}
private static int transform(int value) {
return value ^ (value >>> 16);
}
}
// OSR state monitoring
public static class OSRMonitor {
private static long osrTransitionCount = 0;
private static long compilationCount = 0;
public static void recordOSRTransition() {
osrTransitionCount++;
System.out.println("OSR transition recorded. Total: " + osrTransitionCount);
}
public static void recordCompilation() {
compilationCount++;
}
public static void printStatistics() {
System.out.println("OSR Statistics:");
System.out.println("  Total OSR transitions: " + osrTransitionCount);
System.out.println("  Total compilations: " + compilationCount);
}
}
}

Tiered Compilation and OSR

Step 4: Tiered Compilation Integration

package com.example.osr.tiered;
import java.util.concurrent.atomic.AtomicLong;
public class TieredCompilationOSR {
// Tiered compilation levels
public enum CompilationTier {
INTERPRETED(0),      // Pure interpretation
OSR_SIMPLE(1),       // OSR with C1 compiler
OSR_FULL(2),         // OSR with C2 compiler
SIMPLE(3),           // C1 compiled
FULL(4);             // C2 compiled (highest optimization)
private final int level;
CompilationTier(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
public boolean isBetterThan(CompilationTier other) {
return this.level > other.level;
}
}
// Method compilation state
public static class MethodCompilationState {
private final String methodName;
private volatile CompilationTier currentTier;
private final AtomicLong invocationCount;
private final AtomicLong backedgeCount;
private volatile boolean osrStubInstalled;
public MethodCompilationState(String methodName) {
this.methodName = methodName;
this.currentTier = CompilationTier.INTERPRETED;
this.invocationCount = new AtomicLong(0);
this.backedgeCount = new AtomicLong(0);
this.osrStubInstalled = false;
}
public void recordInvocation() {
long count = invocationCount.incrementAndGet();
checkForTierPromotion(count);
}
public void recordBackedge() {
long count = backedgeCount.incrementAndGet();
checkForOSR(count);
}
private void checkForTierPromotion(long invocationCount) {
// Tiered compilation thresholds
if (currentTier == CompilationTier.INTERPRETED && invocationCount >= 1000) {
promoteToTier(CompilationTier.SIMPLE);
} else if (currentTier == CompilationTier.SIMPLE && invocationCount >= 10000) {
promoteToTier(CompilationTier.FULL);
}
}
private void checkForOSR(long backedgeCount) {
// OSR thresholds
if (!osrStubInstalled && backedgeCount >= 5000) {
installOSRStub();
}
// Upgrade OSR tier
if (osrStubInstalled && backedgeCount >= 20000 && 
currentTier == CompilationTier.OSR_SIMPLE) {
promoteToTier(CompilationTier.OSR_FULL);
}
}
private void promoteToTier(CompilationTier newTier) {
if (newTier.isBetterThan(currentTier)) {
System.out.println("Promoting " + methodName + " from " + 
currentTier + " to " + newTier);
currentTier = newTier;
}
}
private void installOSRStub() {
System.out.println("Installing OSR stub for: " + methodName);
osrStubInstalled = true;
if (currentTier == CompilationTier.INTERPRETED) {
currentTier = CompilationTier.OSR_SIMPLE;
}
}
// Getters
public CompilationTier getCurrentTier() { return currentTier; }
public long getInvocationCount() { return invocationCount.get(); }
public long getBackedgeCount() { return backedgeCount.get(); }
public boolean isOsrStubInstalled() { return osrStubInstalled; }
}
// Demonstration of tiered compilation with OSR
public static class TieredOSRDemo {
public static void runTieredDemo() {
MethodCompilationState methodState = new MethodCompilationState("tieredDemo");
// Simulate method execution with increasing intensity
for (int phase = 1; phase <= 5; phase++) {
System.out.println("\n--- Phase " + phase + " ---");
simulateMethodExecution(methodState, phase * 5000);
printState(methodState);
}
}
private static void simulateMethodExecution(MethodCompilationState state, int iterations) {
for (int i = 0; i < iterations; i++) {
state.recordInvocation();
// Simulate loop backedge every few invocations
if (i % 10 == 0) {
state.recordBackedge();
}
}
}
private static void printState(MethodCompilationState state) {
System.out.println("Method: " + state.getCurrentTier() +
" | Invocations: " + state.getInvocationCount() +
" | Backedges: " + state.getBackedgeCount() +
" | OSR: " + state.isOsrStubInstalled());
}
// Real-world example: Data processing pipeline
public static void processDataWithTieredCompilation(double[] data) {
MethodCompilationState processState = new MethodCompilationState("processData");
// Phase 1: Initial processing (interpreted)
normalizeData(data, processState);
// Phase 2: Heavy computation (likely OSR)
applyTransformations(data, processState);
// Phase 3: Final aggregation (compiled)
aggregateResults(data, processState);
}
private static void normalizeData(double[] data, MethodCompilationState state) {
for (int i = 0; i < data.length; i++) {
state.recordInvocation();
if (i > 0) state.recordBackedge();
data[i] = data[i] / 255.0; // Normalize to [0,1]
}
}
private static void applyTransformations(double[] data, MethodCompilationState state) {
for (int i = 0; i < data.length; i++) {
state.recordInvocation();
if (i > 0) state.recordBackedge();
// Multiple transformations that benefit from optimization
data[i] = Math.log1p(data[i]);
data[i] = Math.sin(data[i] * Math.PI);
data[i] = data[i] * data[i];
}
}
private static void aggregateResults(double[] data, MethodCompilationState state) {
double sum = 0;
for (int i = 0; i < data.length; i++) {
state.recordInvocation();
if (i > 0) state.recordBackedge();
sum += data[i];
}
System.out.println("Final sum: " + sum);
}
}
}

OSR in Modern Java Features

Step 5: OSR with Streams and Lambdas

package com.example.osr.modern;
import java.util.*;
import java.util.stream.*;
import java.util.concurrent.*;
public class ModernJavaOSR {
// OSR with Java Streams
public static class StreamOSR {
// Stream operations that trigger OSR
public static long streamProcessingOSR(List<Integer> numbers) {
return numbers.stream()
.filter(n -> n % 2 == 0)          // Lambda might get OSR
.mapToLong(n -> (long) n * n)     // Another lambda
.sum();                           // Terminal operation
}
// Complex stream pipeline
public static double complexStreamOSR(List<Double> values) {
return values.parallelStream()        // Parallel might have different OSR behavior
.map(x -> Math.log(x + 1.0))      // Hot lambda
.filter(x -> x > 0.0)
.map(x -> x * x)
.reduce(0.0, Double::sum);
}
// Stream with stateful operations
public static List<Integer> statefulStreamOSR(int[] data) {
return Arrays.stream(data)
.boxed()
.sorted()                         // Stateful - might affect OSR
.distinct()                       // Another stateful operation
.collect(Collectors.toList());
}
}
// OSR with CompletableFuture and async operations
public static class AsyncOSR {
public static CompletableFuture<Long> asyncComputationOSR(int n) {
return CompletableFuture.supplyAsync(() -> {
// This lambda runs in ForkJoinPool and can benefit from OSR
long result = 0;
for (int i = 0; i < n; i++) {
result += computeAsync(i);
}
return result;
});
}
private static long computeAsync(int value) {
// Simulate async computation
return ThreadLocalRandom.current().nextLong(100) + value;
}
// Multiple async stages with OSR
public static CompletableFuture<Void> pipelineOSR(List<String> data) {
return CompletableFuture.supplyAsync(() -> processBatch(data))
.thenApplyAsync(ModernJavaOSR::transformBatch)
.thenAcceptAsync(ModernJavaOSR::storeResults)
.exceptionally(throwable -> {
System.err.println("Pipeline failed: " + throwable.getMessage());
return null;
});
}
private static List<String> processBatch(List<String> data) {
return data.stream()
.filter(s -> !s.isEmpty())
.map(String::toUpperCase)
.collect(Collectors.toList());
}
}
// OSR with virtual threads (Project Loom)
public static class VirtualThreadOSR {
public static void virtualThreadDemo() throws Exception {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<Long>> futures = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
final int taskId = i;
Future<Long> future = executor.submit(() -> {
// Each virtual thread might trigger OSR independently
return computeWithOSR(taskId);
});
futures.add(future);
}
long total = 0;
for (Future<Long> future : futures) {
total += future.get();
}
System.out.println("Virtual threads total: " + total);
}
}
private static long computeWithOSR(int base) {
long result = 0;
// This loop in each virtual thread can trigger OSR independently
for (int i = 0; i < 10000; i++) {
result += base * i + complexFunction(i);
}
return result;
}
private static long complexFunction(int n) {
return (long) Math.pow(n % 100, 2);
}
}
// Helper methods
private static List<String> transformBatch(List<String> batch) {
return batch.stream()
.map(s -> "processed_" + s)
.collect(Collectors.toList());
}
private static void storeResults(List<String> results) {
// Simulate storage operation
System.out.println("Storing " + results.size() + " results");
}
}

OSR Performance Impact Analysis

Step 6: Measuring OSR Effects

package com.example.osr.performance;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class OSRPerformanceAnalysis {
// Performance counters for OSR analysis
public static class OSRPerformanceCounters {
private final AtomicLong interpretedTime = new AtomicLong(0);
private final AtomicLong osrTime = new AtomicLong(0);
private final AtomicLong compiledTime = new AtomicLong(0);
private final AtomicLong transitionCount = new AtomicLong(0);
public void recordInterpretedTime(long nanos) {
interpretedTime.addAndGet(nanos);
}
public void recordOSRTime(long nanos) {
osrTime.addAndGet(nanos);
}
public void recordCompiledTime(long nanos) {
compiledTime.addAndGet(nanos);
}
public void recordTransition() {
transitionCount.incrementAndGet();
}
public void printAnalysis() {
long totalTime = interpretedTime.get() + osrTime.get() + compiledTime.get();
System.out.println("OSR Performance Analysis:");
System.out.printf("Interpreted time: %d ns (%.1f%%)%n", 
interpretedTime.get(), (interpretedTime.get() * 100.0 / totalTime));
System.out.printf("OSR time: %d ns (%.1f%%)%n", 
osrTime.get(), (osrTime.get() * 100.0 / totalTime));
System.out.printf("Compiled time: %d ns (%.1f%%)%n", 
compiledTime.get(), (compiledTime.get() * 100.0 / totalTime));
System.out.printf("Total transitions: %d%n", transitionCount.get());
}
}
// Benchmark to measure OSR impact
public static class OSRBenchmark {
public static void runOSRBenchmark() {
OSRPerformanceCounters counters = new OSRPerformanceCounters();
// Warm up to trigger compilation
System.out.println("Warming up...");
for (int i = 0; i < 5; i++) {
benchmarkMethod(1000, counters);
}
// Actual benchmark
System.out.println("Running benchmark...");
long startTime = System.nanoTime();
for (int i = 0; i < 10; i++) {
benchmarkMethod(1000000, counters);
}
long totalTime = System.nanoTime() - startTime;
System.out.printf("Total benchmark time: %.3f ms%n", totalTime / 1_000_000.0);
counters.printAnalysis();
}
private static long benchmarkMethod(int iterations, OSRPerformanceCounters counters) {
long result = 0;
long phaseStart = System.nanoTime();
// Phase 1: Likely interpreted
for (int i = 0; i < Math.min(iterations, 1000); i++) {
result += compute(i);
}
counters.recordInterpretedTime(System.nanoTime() - phaseStart);
// Phase 2: OSR likely triggers here
phaseStart = System.nanoTime();
for (int i = 1000; i < iterations; i++) {
result += compute(i);
// Simulate OSR transition point
if (i == 5000) {
counters.recordTransition();
counters.recordOSRTime(System.nanoTime() - phaseStart);
phaseStart = System.nanoTime();
}
}
if (iterations > 5000) {
counters.recordCompiledTime(System.nanoTime() - phaseStart);
}
return result;
}
private static long compute(int n) {
// Non-trivial computation
return (long) (Math.sin(n) * Math.cos(n) * 1000);
}
}
// Comparing with and without OSR potential
public static class OSRComparison {
public static void compareOSRvsNonOSR() {
int size = 1000000;
long startTime = System.nanoTime();
long result1 = methodWithOSROpportunity(size);
long time1 = System.nanoTime() - startTime;
startTime = System.nanoTime();
long result2 = methodWithoutOSROpportunity(size);
long time2 = System.nanoTime() - startTime;
System.out.println("OSR vs Non-OSR Comparison:");
System.out.printf("With OSR opportunity: %d ns, result: %d%n", time1, result1);
System.out.printf("Without OSR opportunity: %d ns, result: %d%n", time2, result2);
System.out.printf("Speedup: %.2fx%n", (double) time2 / time1);
}
// Method that allows OSR (long-running loop)
private static long methodWithOSROpportunity(int n) {
long sum = 0;
for (int i = 0; i < n; i++) {
sum += i * i - i + 1;
}
return sum;
}
// Method that prevents OSR (short method calls)
private static long methodWithoutOSROpportunity(int n) {
long total = 0;
int chunkSize = 1000;
int chunks = n / chunkSize;
for (int chunk = 0; chunk < chunks; chunk++) {
total += processChunk(chunk * chunkSize, Math.min((chunk + 1) * chunkSize, n));
}
return total;
}
private static long processChunk(int start, int end) {
long chunkSum = 0;
for (int i = start; i < end; i++) {
chunkSum += i * i - i + 1;
}
return chunkSum;
}
}
}

OSR in Real-World Applications

Step 7: Practical OSR Use Cases

package com.example.osr.practical;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
public class PracticalOSRUseCases {
// 1. Scientific Computing
public static class ScientificComputing {
// Numerical integration with OSR
public static double integrate(Function<Double, Double> function, 
double a, double b, int steps) {
double sum = 0.0;
double dx = (b - a) / steps;
for (int i = 0; i < steps; i++) {
double x = a + i * dx;
sum += function.apply(x) * dx;
// This loop becomes hot quickly for large steps
if (i % 10000 == 0 && i > 0) {
System.out.println("Progress: " + (i * 100.0 / steps) + "%");
}
}
return sum;
}
// Matrix multiplication with OSR
public static double[][] matrixMultiply(double[][] a, double[][] b) {
int n = a.length;
int m = b[0].length;
int p = b.length;
double[][] result = new double[n][m];
// Triple nested loop - prime candidate for OSR
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
double sum = 0.0;
for (int k = 0; k < p; k++) {
sum += a[i][k] * b[k][j];
}
result[i][j] = sum;
}
}
return result;
}
}
// 2. Data Processing
public static class DataProcessing {
// Large dataset processing with OSR
public static Map<String, Integer> processLogFiles(List<String> logs, 
Set<String> keywords) {
Map<String, Integer> keywordCounts = new ConcurrentHashMap<>();
logs.parallelStream().forEach(log -> {
// Each parallel task can benefit from OSR independently
processSingleLog(log, keywords, keywordCounts);
});
return keywordCounts;
}
private static void processSingleLog(String log, Set<String> keywords,
Map<String, Integer> counts) {
String[] words = log.toLowerCase().split("\\W+");
for (String word : words) {
if (keywords.contains(word)) {
counts.merge(word, 1, Integer::sum);
}
}
}
// Real-time data stream processing
public static class StreamingProcessor {
private final BlockingQueue<DataPoint> queue;
private volatile boolean running;
private long processedCount;
public StreamingProcessor() {
this.queue = new LinkedBlockingQueue<>(10000);
this.running = true;
this.processedCount = 0;
}
public void startProcessing() {
Thread processorThread = new Thread(this::processLoop);
processorThread.setDaemon(true);
processorThread.start();
}
private void processLoop() {
// This long-running loop benefits greatly from OSR
while (running) {
try {
DataPoint point = queue.poll(100, TimeUnit.MILLISECONDS);
if (point != null) {
processDataPoint(point);
processedCount++;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
private void processDataPoint(DataPoint point) {
// Complex processing that becomes optimized via OSR
double value = point.getValue();
for (int i = 0; i < 100; i++) {
value = Math.sin(value) * Math.cos(value) + 1.0;
}
point.setProcessedValue(value);
}
public void submitData(DataPoint point) {
queue.offer(point);
}
public void stop() {
running = false;
}
public long getProcessedCount() {
return processedCount;
}
}
}
// 3. Game Development
public static class GameEngine {
// Game loop with OSR optimization
public static class GameLoop {
private static final int TPS = 60; // Ticks per second
private static final long NANOS_PER_TICK = 1_000_000_000 / TPS;
private volatile boolean running;
private long tickCount;
public void start() {
running = true;
tickCount = 0;
long lastTime = System.nanoTime();
double delta = 0;
// Main game loop - prime candidate for OSR
while (running) {
long currentTime = System.nanoTime();
delta += (currentTime - lastTime) / (double) NANOS_PER_TICK;
lastTime = currentTime;
while (delta >= 1) {
tick();
delta--;
}
render(delta);
// Yield to prevent busy-waiting
Thread.yield();
}
}
private void tick() {
tickCount++;
// Update game state
updatePhysics();
updateAI();
updateCollisions();
if (tickCount % 1000 == 0) {
System.out.println("Tick: " + tickCount);
}
}
private void updatePhysics() {
// Physics calculations that benefit from OSR
for (int i = 0; i < 1000; i++) {
// Simulate physics calculations
double result = complexPhysicsCalculation(i);
}
}
private void updateAI() {
// AI calculations
for (int i = 0; i < 500; i++) {
// Simulate AI decision making
complexAICalculation(i);
}
}
private void updateCollisions() {
// Collision detection
for (int i = 0; i < 200; i++) {
// Simulate collision checks
complexCollisionCheck(i);
}
}
private void render(double interpolation) {
// Rendering logic
}
private double complexPhysicsCalculation(int index) {
return Math.sin(index * 0.1) * Math.cos(index * 0.1);
}
private void complexAICalculation(int index) {
// Simulate AI logic
double decision = Math.random() * index;
}
private boolean complexCollisionCheck(int index) {
// Simulate collision detection
return (index % 17) == 0;
}
public void stop() {
running = false;
}
}
}
// Data classes
public static class DataPoint {
private final long timestamp;
private double value;
private double processedValue;
public DataPoint(double value) {
this.timestamp = System.currentTimeMillis();
this.value = value;
}
public double getValue() { return value; }
public void setProcessedValue(double processedValue) { this.processedValue = processedValue; }
public long getTimestamp() { return timestamp; }
}
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
}

OSR Troubleshooting and Best Practices

Step 8: OSR Optimization Guidelines

package com.example.osr.bestpractices;
import java.util.*;
import java.util.concurrent.*;
public class OSRBestPractices {
// Patterns that benefit from OSR
public static class OSRFriendlyPatterns {
// 1. Long-running loops with substantial computation
public static long osrFriendlyLoop(int iterations) {
long result = 0;
for (int i = 0; i < iterations; i++) {
// Substantial computation per iteration
result += complexOperation(i);
// Avoid external calls that might inhibit optimization
// result += externalMethod(i); // ❌ Avoid in hot loops
}
return result;
}
// 2. Loops with predictable control flow
public static void predictableControlFlow(int[] data) {
for (int i = 0; i < data.length; i++) {
// Predictable if-condition
if (data[i] > 0) {  // ✅ Predictable
data[i] = processPositive(data[i]);
} else {
data[i] = processNegative(data[i]);
}
}
}
// 3. Methods with focused, hot code regions
public static double focusedHotRegion(double[] values) {
double sum = 0;
double sumSquares = 0;
// Hot region - keep it focused
for (double value : values) {
sum += value;
sumSquares += value * value;
}
// Less critical computations outside hot region
double mean = sum / values.length;
double variance = sumSquares / values.length - mean * mean;
return Math.sqrt(variance);
}
private static long complexOperation(int n) {
// Keep this method small and inline-friendly
return (long) n * n + 2 * n + 1;
}
private static int processPositive(int value) {
return value * 2;
}
private static int processNegative(int value) {
return -value;
}
}
// Patterns to avoid for OSR
public static class OSRUnfriendlyPatterns {
// 1. Loops with frequent external calls
public static long unfriendlyExternalCalls(int iterations) {
long result = 0;
for (int i = 0; i < iterations; i++) {
// External call inhibits optimization
result += SomeExternalService.compute(i); // ❌ Inhibits OSR
}
return result;
}
// 2. Overly complex loop bodies
public static void overlyComplexLoop(int[] data) {
for (int i = 0; i < data.length; i++) {
// Too many different operations
data[i] = transformA(data[i]);
data[i] = transformB(data[i]);
data[i] = transformC(data[i]);
data[i] = transformD(data[i]);
data[i] = transformE(data[i]); // ❌ Too complex
// Better: Break into separate focused loops
}
}
// 3. Unpredictable control flow
public static void unpredictableBranching(int[] data, Random random) {
for (int i = 0; i < data.length; i++) {
// Unpredictable condition
if (random.nextBoolean()) { // ❌ Unpredictable
data[i] = processOptionA(data[i]);
} else {
data[i] = processOptionB(data[i]);
}
}
}
// 4. Loop with exception handling
public static void loopWithExceptions(List<String> data) {
for (String item : data) {
try {
processItem(item); // ❌ Exception handling inhibits
} catch (Exception e) {
// Handle exception
}
}
}
private static int transformA(int value) { return value + 1; }
private static int transformB(int value) { return value * 2; }
private static int transformC(int value) { return value - 1; }
private static int transformD(int value) { return value / 2; }
private static int transformE(int value) { return value % 10; }
private static int processOptionA(int value) { return value * 3; }
private static int processOptionB(int value) { return value / 3; }
private static void processItem(String item) { /* processing */ }
}
// External service simulation
public static class SomeExternalService {
public static long compute(int n) {
return n * 2L;
}
}
// OSR monitoring and debugging
public static class OSRDiagnostics {
public static void enableOSRDebugging() {
// JVM flags for OSR debugging (would be set as -XX parameters)
System.setProperty("jvm.debug.OSR", "true");
System.setProperty("jvm.print.compilation", "true");
System.setProperty("jvm.print.OSR", "true");
}
public static void monitorOSRBehavior() {
Thread monitoringThread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
// Monitor compilation events
printCompilationStats();
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
monitoringThread.setDaemon(true);
monitoringThread.start();
}
private static void printCompilationStats() {
// In real implementation, would use JVM MX beans
System.out.println("OSR Monitoring - " + new Date());
// Print relevant statistics
}
// Method to identify OSR opportunities
public static void analyzeMethodForOSR(String methodName, Runnable method) {
long startTime = System.nanoTime();
method.run();
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.printf("Method %s took %d ns%n", methodName, duration);
if (duration > 1_000_000) { // 1ms threshold
System.out.println("  ⚡ Potential OSR candidate - long execution time");
}
}
}
// Best practices summary
public static class OSRGuidelines {
public static void printBestPractices() {
System.out.println("""
OSR Best Practices:
1. WRITE FOCUSED LOOPS
- Keep loop bodies focused on computation
- Avoid external method calls in hot loops
2. MINIMIZE BRANCH COMPLEXITY
- Use predictable conditions when possible
- Avoid complex control flow in hot loops
3. OPTIMIZE DATA ACCESS
- Use local variables for frequently accessed data
- Prefer primitive arrays over collections in critical loops
4. AVOID INHIBITING PATTERNS
- Remove exception handling from hot loops
- Minimize object allocation in performance-critical code
- Avoid synchronization in compute-intensive sections
5. ENABLE TIERED COMPILATION
- Use -XX:+TieredCompilation (enabled by default)
- Allow both C1 and C2 optimizations
6. MONITOR AND MEASURE
- Use -XX:+PrintCompilation to see OSR events
- Profile to identify actual hot spots
""");
}
}
}

Key Takeaways

OSR Benefits:

  1. Immediate Performance: Optimizes long-running methods without waiting for completion
  2. Adaptive Optimization: Responds to actual runtime behavior
  3. Progressive Optimization: Works with tiered compilation for gradual improvement

When OSR Matters:

  1. Long-running loops with substantial computation per iteration
  2. Server applications with continuous operation
  3. Scientific computing with intensive numerical methods
  4. Game engines with tight main loops
  5. Data processing pipelines handling large datasets

Optimization Guidelines:

  1. Focus hot loops - Keep critical sections clean and computation-focused
  2. Avoid inhibitors - Minimize external calls, exceptions, and complex control flow
  3. Enable monitoring - Use JVM flags to observe OSR behavior
  4. Profile realistically - Test with realistic data sizes and usage patterns

OSR is a powerful JVM feature that enables dynamic optimization of running code, particularly benefiting applications with long-running loops and continuous operation patterns.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper