A complete guide to integrating and using CPU profilers in Java applications for performance optimization and bottleneck identification.
Overview of Java CPU Profiling
CPU profilers help identify performance bottlenecks by analyzing which methods consume the most CPU time. This guide covers multiple profiling approaches and integrations.
Table of Contents
- Built-in Profiling Tools
- Third-Party Profiler Integration
- Programmatic Profiling
- Production Profiling
- Analysis and Optimization
Built-in Profiling Tools
Java Flight Recorder (JFR)
package com.profiler.jfr;
import jdk.jfr.*;
import java.time.Duration;
// Custom JFR event for method profiling
@Name("com.example.MethodExecution")
@Label("Method Execution")
@Description("Tracks method execution time")
class MethodExecutionEvent extends Event {
@Label("Method Name")
public String methodName;
@Label("Class Name")
public String className;
@Label("Execution Time")
@Timespan
public long executionTime;
@Label("Thread Name")
public String threadName;
}
public class JFRProfiler {
public static void startJFRRecording(String filename) {
try {
// Configure JFR recording
Configuration config = Configuration.getConfiguration("default");
Recording recording = new Recording(config);
recording.setDestination(new File(filename));
recording.setDumpOnExit(true);
recording.setToDisk(true);
// Enable specific events
recording.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
recording.enable("jdk.GCHeapSummary").withPeriod(Duration.ofSeconds(1));
recording.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(10));
recording.start();
System.out.println("JFR recording started: " + filename);
} catch (Exception e) {
System.err.println("Failed to start JFR recording: " + e.getMessage());
}
}
// Method profiling with custom JFR events
public void profiledMethod(String input) {
long startTime = System.nanoTime();
MethodExecutionEvent event = new MethodExecutionEvent();
try {
event.begin();
event.methodName = "profiledMethod";
event.className = this.getClass().getName();
event.threadName = Thread.currentThread().getName();
// Simulate work
Thread.sleep(100);
processInput(input);
} catch (Exception e) {
event.commit();
} finally {
event.executionTime = System.nanoTime() - startTime;
event.commit();
}
}
private void processInput(String input) {
// Method implementation
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) throws Exception {
// Start continuous profiling
startJFRRecording("myapp.jfr");
JFRProfiler profiler = new JFRProfiler();
for (int i = 0; i < 1000; i++) {
profiler.profiledMethod("test-" + i);
}
Thread.sleep(5000); // Keep running to capture data
}
}
Java Mission Control Integration
package com.profiler.jmc;
import javax.management.*;
import java.lang.management.*;
public class MissionControlIntegration {
public static void setupJMXMonitoring() {
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.profiler:type=PerformanceMonitor");
PerformanceMonitor mbean = new PerformanceMonitor();
mbs.registerMBean(mbean, name);
System.out.println("Performance MBean registered");
} catch (Exception e) {
System.err.println("Failed to setup JMX monitoring: " + e.getMessage());
}
}
public static class PerformanceMonitor implements PerformanceMonitorMBean {
private long totalRequests = 0;
private long totalProcessingTime = 0;
private double averageResponseTime = 0.0;
@Override
public long getTotalRequests() {
return totalRequests;
}
@Override
public double getAverageResponseTime() {
return averageResponseTime;
}
@Override
public void recordRequest(long processingTime) {
totalRequests++;
totalProcessingTime += processingTime;
averageResponseTime = (double) totalProcessingTime / totalRequests;
}
@Override
public void resetStats() {
totalRequests = 0;
totalProcessingTime = 0;
averageResponseTime = 0.0;
}
}
public interface PerformanceMonitorMBean {
long getTotalRequests();
double getAverageResponseTime();
void recordRequest(long processingTime);
void resetStats();
}
}
Third-Party Profiler Integration
Async Profiler Integration
package com.profiler.async;
import java.io.*;
import java.nio.file.*;
import java.util.concurrent.*;
public class AsyncProfilerIntegration {
private static final String PROFILER_LIB = "./libasyncProfiler.so";
private Process profilerProcess;
private final BlockingQueue<String> commandQueue = new LinkedBlockingQueue<>();
public void startProfiling(String outputFile, int durationSeconds) {
try {
// Build profiler command
List<String> command = new ArrayList<>();
command.add("java");
command.add("-agentpath:" + PROFILER_LIB + "=start,event=cpu,file=" + outputFile);
command.add("-jar");
command.add("myapp.jar");
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream(true);
profilerProcess = pb.start();
// Monitor profiler output
startOutputReader();
System.out.println("Async Profiler started with output: " + outputFile);
// Schedule stop
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(this::stopProfiling, durationSeconds, TimeUnit.SECONDS);
} catch (Exception e) {
System.err.println("Failed to start Async Profiler: " + e.getMessage());
}
}
public void startProfilingForPid(int pid, String outputFile) {
try {
List<String> command = new ArrayList<>();
command.add(PROFILER_LIB);
command.add("-d");
command.add("60"); // duration 60 seconds
command.add("-f");
command.add(outputFile);
command.add("-e");
command.add("cpu");
command.add(Integer.toString(pid));
ProcessBuilder pb = new ProcessBuilder(command);
profilerProcess = pb.start();
startOutputReader();
} catch (Exception e) {
System.err.println("Failed to profile PID " + pid + ": " + e.getMessage());
}
}
private void startOutputReader() {
Thread readerThread = new Thread(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(profilerProcess.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("[AsyncProfiler] " + line);
}
} catch (IOException e) {
System.err.println("Error reading profiler output: " + e.getMessage());
}
});
readerThread.setDaemon(true);
readerThread.start();
}
public void stopProfiling() {
if (profilerProcess != null) {
profilerProcess.destroy();
try {
profilerProcess.waitFor();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Async Profiler stopped");
}
}
public String getFlameGraph(String jfrFile) {
try {
// Convert JFR to flame graph
List<String> command = new ArrayList<>();
command.add(PROFILER_LIB);
command.add("-f");
command.add(jfrFile);
command.add("-o");
command.add("flamegraph");
command.add("-t");
ProcessBuilder pb = new ProcessBuilder(command);
Process process = pb.start();
StringBuilder output = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
}
process.waitFor();
return output.toString();
} catch (Exception e) {
System.err.println("Failed to generate flame graph: " + e.getMessage());
return null;
}
}
}
JProfiler Integration
package com.profiler.jprofiler;
import java.lang.management.*;
import javax.management.*;
public class JProfilerIntegration {
public static void setupJProfilerTelemetry() {
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
// Register custom metrics for JProfiler
registerCustomMetrics(mbs);
// Setup JMX notifications for critical events
setupJMXNotifications(mbs);
System.out.println("JProfiler telemetry configured");
} catch (Exception e) {
System.err.println("Failed to setup JProfiler telemetry: " + e.getMessage());
}
}
private static void registerCustomMetrics(MBeanServer mbs) throws Exception {
ObjectName name = new ObjectName("com.profiler:type=CustomMetrics");
CustomMetricsMBean mbean = new CustomMetrics();
mbs.registerMBean(mbean, name);
}
private static void setupJMXNotifications(MBeanServer mbs) throws Exception {
NotificationEmitter threadMXBean = (NotificationEmitter) ManagementFactory.getThreadMXBean();
threadMXBean.addNotificationListener((notification, handback) -> {
if (notification instanceof ThreadNotification) {
ThreadNotification tn = (ThreadNotification) notification;
System.out.println("Thread event: " + tn.getThreadInfo().getThreadName() +
" - " + tn.getType());
}
}, null, null);
}
public static class CustomMetrics implements CustomMetricsMBean {
private int activeConnections = 0;
private int cacheHitRate = 0;
private long totalMemoryUsed = 0;
@Override
public int getActiveConnections() {
return activeConnections;
}
@Override
public int getCacheHitRate() {
return cacheHitRate;
}
@Override
public long getTotalMemoryUsed() {
return totalMemoryUsed;
}
@Override
public void setActiveConnections(int connections) {
this.activeConnections = connections;
}
@Override
public void setCacheHitRate(int hitRate) {
this.cacheHitRate = hitRate;
}
@Override
public void setTotalMemoryUsed(long memoryUsed) {
this.totalMemoryUsed = memoryUsed;
}
}
public interface CustomMetricsMBean {
int getActiveConnections();
int getCacheHitRate();
long getTotalMemoryUsed();
void setActiveConnections(int connections);
void setCacheHitRate(int hitRate);
void setTotalMemoryUsed(long memoryUsed);
}
}
Programmatic Profiling
Custom Method-Level Profiler
package com.profiler.custom;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class MethodProfiler {
private static final ConcurrentHashMap<String, MethodStats> methodStats = new ConcurrentHashMap<>();
private static final ThreadLocal<Deque<MethodCall>> callStack = ThreadLocal.withInitial(ArrayDeque::new);
private static final AtomicBoolean enabled = new AtomicBoolean(false);
public static void start() {
enabled.set(true);
}
public static void stop() {
enabled.set(false);
}
public static void profile(String methodName, Runnable code) {
if (!enabled.get()) {
code.run();
return;
}
long startTime = System.nanoTime();
try {
code.run();
} finally {
long duration = System.nanoTime() - startTime;
recordMethodCall(methodName, duration);
}
}
public static <T> T profile(String methodName, Callable<T> code) {
if (!enabled.get()) {
try {
return code.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
long startTime = System.nanoTime();
try {
return code.call();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
long duration = System.nanoTime() - startTime;
recordMethodCall(methodName, duration);
}
}
private static void recordMethodCall(String methodName, long duration) {
methodStats.compute(methodName, (key, stats) -> {
if (stats == null) {
stats = new MethodStats();
}
stats.recordCall(duration);
return stats;
});
}
public static void printStats() {
System.out.println("\n=== Method Profiling Statistics ===");
methodStats.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue().totalTime, a.getValue().totalTime))
.forEach(entry -> {
MethodStats stats = entry.getValue();
System.out.printf("%s: calls=%d, total=%.3fms, avg=%.3fms, max=%.3fms%n",
entry.getKey(),
stats.callCount,
stats.totalTime / 1_000_000.0,
stats.getAverageTime() / 1_000_000.0,
stats.maxTime / 1_000_000.0);
});
}
public static Map<String, MethodStats> getStats() {
return new HashMap<>(methodStats);
}
public static void reset() {
methodStats.clear();
}
public static class MethodStats {
private long callCount = 0;
private long totalTime = 0;
private long maxTime = 0;
private final LongAdder currentCalls = new LongAdder();
public void recordCall(long duration) {
callCount++;
totalTime += duration;
maxTime = Math.max(maxTime, duration);
currentCalls.increment();
}
public long getCallCount() { return callCount; }
public long getTotalTime() { return totalTime; }
public long getMaxTime() { return maxTime; }
public double getAverageTime() {
return callCount == 0 ? 0 : (double) totalTime / callCount;
}
public long getCurrentCalls() { return currentCalls.sum(); }
}
public static class MethodCall {
final String methodName;
final long startTime;
MethodCall(String methodName) {
this.methodName = methodName;
this.startTime = System.nanoTime();
}
}
}
Annotation-Based Profiling
package com.profiler.annotations;
import java.lang.annotation.*;
import java.lang.reflect.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Profile {
String name() default "";
boolean logParameters() default false;
boolean logReturnValue() default false;
}
public class ProfilingAspect {
private static final ThreadLocal<Boolean> profilingEnabled = ThreadLocal.withInitial(() -> true);
public static Object profileMethod(ProceedingJoinPoint joinPoint) throws Throwable {
if (!profilingEnabled.get()) {
return joinPoint.proceed();
}
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Profile profile = method.getAnnotation(Profile.class);
String methodName = profile.name().isEmpty() ? method.getName() : profile.name();
long startTime = System.nanoTime();
try {
Object result = joinPoint.proceed();
if (profile.logReturnValue()) {
System.out.println(methodName + " returned: " + result);
}
return result;
} finally {
long duration = System.nanoTime() - startTime;
MethodProfiler.recordMethodCall(methodName, duration);
if (profile.logParameters()) {
System.out.println(methodName + " called with: " +
Arrays.toString(joinPoint.getArgs()));
}
}
}
public static void enableProfiling() {
profilingEnabled.set(true);
}
public static void disableProfiling() {
profilingEnabled.set(false);
}
}
// Usage example
@Service
class BusinessService {
@Profile(name = "processOrder", logParameters = true, logReturnValue = true)
public Order processOrder(OrderRequest request) {
// Business logic
return new Order();
}
@Profile(name = "calculateTax")
public double calculateTax(double amount, String country) {
// Tax calculation logic
return amount * 0.2;
}
}
Production Profiling
Low-Overhead Sampling Profiler
package com.profiler.sampling;
import java.lang.management.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class SamplingProfiler {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
private final Map<String, AtomicLong> methodSamples = new ConcurrentHashMap<>();
private final long samplingIntervalMs;
private volatile boolean running = false;
public SamplingProfiler(long samplingIntervalMs) {
this.samplingIntervalMs = samplingIntervalMs;
// Enable CPU time measurement
threadMXBean.setThreadCpuTimeEnabled(true);
}
public void start() {
if (running) return;
running = true;
scheduler.scheduleAtFixedRate(this::sample, 0, samplingIntervalMs, TimeUnit.MILLISECONDS);
System.out.println("Sampling profiler started with interval: " + samplingIntervalMs + "ms");
}
public void stop() {
running = false;
scheduler.shutdown();
System.out.println("Sampling profiler stopped");
}
private void sample() {
try {
Map<Long, ThreadInfo> threadInfos = getThreadInfos();
for (ThreadInfo threadInfo : threadInfos.values()) {
if (threadInfo != null && isApplicationThread(threadInfo)) {
StackTraceElement[] stackTrace = threadInfo.getStackTrace();
if (stackTrace.length > 0) {
// Record the top method in stack
String methodName = getMethodName(stackTrace[0]);
methodSamples.computeIfAbsent(methodName, k -> new AtomicLong()).incrementAndGet();
}
}
}
} catch (Exception e) {
System.err.println("Error during sampling: " + e.getMessage());
}
}
private Map<Long, ThreadInfo> getThreadInfos() {
long[] threadIds = threadMXBean.getAllThreadIds();
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadIds, 1); // Max depth 1
Map<Long, ThreadInfo> result = new HashMap<>();
for (int i = 0; i < threadIds.length; i++) {
result.put(threadIds[i], threadInfos[i]);
}
return result;
}
private boolean isApplicationThread(ThreadInfo threadInfo) {
String threadName = threadInfo.getThreadName();
return !threadName.startsWith("pool-") &&
!threadName.startsWith("ForkJoinPool") &&
!threadName.equals("main") &&
!threadName.startsWith("Finalizer") &&
!threadName.startsWith("Reference Handler");
}
private String getMethodName(StackTraceElement element) {
return element.getClassName() + "." + element.getMethodName();
}
public void printSamplingResults() {
System.out.println("\n=== Sampling Profiler Results ===");
long totalSamples = methodSamples.values().stream()
.mapToLong(AtomicLong::get)
.sum();
methodSamples.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue().get(), a.getValue().get()))
.forEach(entry -> {
long samples = entry.getValue().get();
double percentage = (samples * 100.0) / totalSamples;
System.out.printf("%s: %.2f%% (%d samples)%n",
entry.getKey(), percentage, samples);
});
}
public Map<String, Long> getSamplingResults() {
Map<String, Long> results = new HashMap<>();
methodSamples.forEach((method, count) -> results.put(method, count.get()));
return results;
}
public void reset() {
methodSamples.clear();
}
}
Continuous Production Profiler
package com.profiler.continuous;
import java.time.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class ContinuousProfiler {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
private final Map<String, ProfilerMetric> metrics = new ConcurrentHashMap<>();
private final long reportingIntervalMs;
private volatile boolean running = false;
public ContinuousProfiler(long reportingIntervalMs) {
this.reportingIntervalMs = reportingIntervalMs;
}
public void start() {
if (running) return;
running = true;
// Schedule metric collection
scheduler.scheduleAtFixedRate(this::collectMetrics, 0, 1000, TimeUnit.MILLISECONDS);
// Schedule reporting
scheduler.scheduleAtFixedRate(this::reportMetrics,
reportingIntervalMs, reportingIntervalMs, TimeUnit.MILLISECONDS);
System.out.println("Continuous profiler started");
}
public void stop() {
running = false;
scheduler.shutdown();
System.out.println("Continuous profiler stopped");
}
private void collectMetrics() {
try {
// Collect JVM metrics
collectJVMMetrics();
// Collect application-specific metrics
collectApplicationMetrics();
} catch (Exception e) {
System.err.println("Error collecting metrics: " + e.getMessage());
}
}
private void collectJVMMetrics() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long maxMemory = runtime.maxMemory();
recordMetric("jvm.memory.used", usedMemory);
recordMetric("jvm.memory.max", maxMemory);
recordMetric("jvm.threads.active", Thread.activeCount());
}
private void collectApplicationMetrics() {
// Collect custom application metrics
recordMetric("app.requests.active", getActiveRequests());
recordMetric("app.cache.hit_rate", getCacheHitRate());
recordMetric("app.db.connections.active", getActiveDBConnections());
}
private void recordMetric(String name, long value) {
metrics.computeIfAbsent(name, k -> new ProfilerMetric())
.record(value);
}
private void reportMetrics() {
System.out.println("\n=== Continuous Profiler Report ===");
System.out.println("Timestamp: " + Instant.now());
metrics.forEach((name, metric) -> {
ProfilerMetric.Snapshot snapshot = metric.getSnapshot();
System.out.printf("%s: count=%d, avg=%.2f, min=%d, max=%d, 95th=%.2f%n",
name, snapshot.count, snapshot.average, snapshot.min,
snapshot.max, snapshot.percentile95);
});
// Reset metrics for next interval
metrics.values().forEach(ProfilerMetric::reset);
}
// Mock methods for demonstration
private long getActiveRequests() { return ThreadLocalRandom.current().nextLong(100, 1000); }
private long getCacheHitRate() { return ThreadLocalRandom.current().nextLong(80, 95); }
private long getActiveDBConnections() { return ThreadLocalRandom.current().nextLong(5, 50); }
public static class ProfilerMetric {
private final LongAdder count = new LongAdder();
private final LongAdder sum = new LongAdder();
private final AtomicLong min = new AtomicLong(Long.MAX_VALUE);
private final AtomicLong max = new AtomicLong(Long.MIN_VALUE);
private final List<Long> values = new CopyOnWriteArrayList<>();
public void record(long value) {
count.increment();
sum.add(value);
min.updateAndGet(current -> Math.min(current, value));
max.updateAndGet(current -> Math.max(current, value));
values.add(value);
}
public Snapshot getSnapshot() {
long cnt = count.sum();
long total = sum.sum();
double avg = cnt == 0 ? 0 : (double) total / cnt;
// Calculate 95th percentile
Collections.sort(values);
int index = (int) Math.ceil(0.95 * values.size());
double percentile95 = index < values.size() ? values.get(index) : 0;
return new Snapshot(cnt, avg, min.get(), max.get(), percentile95);
}
public void reset() {
count.reset();
sum.reset();
min.set(Long.MAX_VALUE);
max.set(Long.MIN_VALUE);
values.clear();
}
public static class Snapshot {
public final long count;
public final double average;
public final long min;
public final long max;
public final double percentile95;
public Snapshot(long count, double average, long min, long max, double percentile95) {
this.count = count;
this.average = average;
this.min = min;
this.max = max;
this.percentile95 = percentile95;
}
}
}
}
Analysis and Optimization
Performance Analysis Toolkit
package com.profiler.analysis;
import java.util.*;
import java.util.stream.*;
public class PerformanceAnalyzer {
public static void analyzeHotMethods(Map<String, MethodProfiler.MethodStats> stats) {
System.out.println("\n=== Hot Methods Analysis ===");
// Identify methods with highest total time
List<Map.Entry<String, MethodProfiler.MethodStats>> hotMethods = stats.entrySet().stream()
.filter(entry -> entry.getValue().getTotalTime() > 1_000_000_000L) // > 1 second total
.sorted((a, b) -> Long.compare(b.getValue().getTotalTime(), a.getValue().getTotalTime()))
.limit(10)
.collect(Collectors.toList());
System.out.println("Top 10 hot methods by total time:");
hotMethods.forEach(entry -> {
MethodProfiler.MethodStats methodStats = entry.getValue();
System.out.printf(" %s: %.3fs total, %d calls, %.3fms avg%n",
entry.getKey(),
methodStats.getTotalTime() / 1_000_000_000.0,
methodStats.getCallCount(),
methodStats.getAverageTime() / 1_000_000.0);
});
// Identify methods with highest average time
List<Map.Entry<String, MethodProfiler.MethodStats>> slowMethods = stats.entrySet().stream()
.filter(entry -> entry.getValue().getCallCount() > 10)
.sorted((a, b) -> Double.compare(
b.getValue().getAverageTime(), a.getValue().getAverageTime()))
.limit(10)
.collect(Collectors.toList());
System.out.println("\nTop 10 slow methods by average time:");
slowMethods.forEach(entry -> {
MethodProfiler.MethodStats methodStats = entry.getValue();
System.out.printf(" %s: %.3fms avg, %d calls%n",
entry.getKey(),
methodStats.getAverageTime() / 1_000_000.0,
methodStats.getCallCount());
});
}
public static void identifyBottlenecks(Map<String, MethodProfiler.MethodStats> stats) {
System.out.println("\n=== Potential Bottlenecks ===");
stats.entrySet().stream()
.filter(entry -> {
MethodProfiler.MethodStats methodStats = entry.getValue();
return methodStats.getCallCount() > 1000 &&
methodStats.getAverageTime() > 10_000_000L; // > 10ms average
})
.forEach(entry -> {
System.out.printf(" High frequency slow method: %s (%.3fms avg, %d calls)%n",
entry.getKey(),
entry.getValue().getAverageTime() / 1_000_000.0,
entry.getValue().getCallCount());
});
}
public static void generateOptimizationSuggestions(Map<String, MethodProfiler.MethodStats> stats) {
System.out.println("\n=== Optimization Suggestions ===");
stats.entrySet().stream()
.filter(entry -> entry.getValue().getCallCount() > 100)
.forEach(entry -> {
String methodName = entry.getKey();
MethodProfiler.MethodStats methodStats = entry.getValue();
if (methodStats.getAverageTime() > 100_000_000L) { // > 100ms average
System.out.printf(" Consider optimizing: %s (%.3fms average)%n",
methodName, methodStats.getAverageTime() / 1_000_000.0);
// Specific suggestions based on method name patterns
if (methodName.contains("Database") || methodName.contains("Query")) {
System.out.println(" * Review database queries and indexes");
System.out.println(" * Consider implementing caching");
}
if (methodName.contains("File") || methodName.contains("IO")) {
System.out.println(" * Review I/O operations");
System.out.println(" * Consider buffering or async I/O");
}
if (methodName.contains("Network") || methodName.contains("HTTP")) {
System.out.println(" * Review network calls");
System.out.println(" * Consider connection pooling");
System.out.println(" * Implement timeouts and retries");
}
}
if (methodStats.getCallCount() > 10_000) {
System.out.printf(" High call frequency: %s (%d calls)%n",
methodName, methodStats.getCallCount());
if (methodName.contains("String") || methodName.contains("concat")) {
System.out.println(" * Consider using StringBuilder");
}
if (methodName.contains("Collection") || methodName.contains("List")) {
System.out.println(" * Review data structures and algorithms");
System.out.println(" * Consider using more efficient collections");
}
}
});
}
}
Main Application with Integrated Profiling
package com.profiler.main;
import com.profiler.custom.MethodProfiler;
import com.profiler.sampling.SamplingProfiler;
import com.profiler.continuous.ContinuousProfiler;
import com.profiler.analysis.PerformanceAnalyzer;
public class ProfilerApplication {
private static final SamplingProfiler samplingProfiler = new SamplingProfiler(10); // 10ms interval
private static final ContinuousProfiler continuousProfiler = new ContinuousProfiler(60000); // 1 minute interval
public static void main(String[] args) throws Exception {
System.out.println("Starting CPU Profiler Integration Demo");
// Start different profilers
startProfilers();
// Run application with profiling
runProfiledApplication();
// Stop profilers and generate reports
stopProfilersAndReport();
}
private static void startProfilers() {
// Start custom method profiling
MethodProfiler.start();
// Start sampling profiler
samplingProfiler.start();
// Start continuous production profiler
continuousProfiler.start();
System.out.println("All profilers started");
}
private static void runProfiledApplication() throws InterruptedException {
DemoApplication app = new DemoApplication();
// Run various operations to generate profiling data
for (int i = 0; i < 100; i++) {
app.processData("batch-" + i);
app.calculateMetrics();
app.performDatabaseOperations();
if (i % 10 == 0) {
app.expensiveOperation();
}
Thread.sleep(50);
}
}
private static void stopProfilersAndReport() {
// Stop profilers
MethodProfiler.stop();
samplingProfiler.stop();
continuousProfiler.stop();
// Generate reports
System.out.println("\n" + "=".repeat(50));
System.out.println("PROFILING REPORTS");
System.out.println("=".repeat(50));
// Method profiling report
MethodProfiler.printStats();
// Sampling profiler report
samplingProfiler.printSamplingResults();
// Performance analysis
PerformanceAnalyzer.analyzeHotMethods(MethodProfiler.getStats());
PerformanceAnalyzer.identifyBottlenecks(MethodProfiler.getStats());
PerformanceAnalyzer.generateOptimizationSuggestions(MethodProfiler.getStats());
}
static class DemoApplication {
public void processData(String data) {
MethodProfiler.profile("processData", () -> {
// Simulate data processing
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(10, 50));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
validateData(data);
transformData(data);
return null;
});
}
public void calculateMetrics() {
MethodProfiler.profile("calculateMetrics", () -> {
// Simulate metric calculations
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(5, 20));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
for (int i = 0; i < 1000; i++) {
Math.sqrt(i) * Math.log(i + 1);
}
return null;
});
}
public void performDatabaseOperations() {
MethodProfiler.profile("performDatabaseOperations", () -> {
// Simulate database operations
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(20, 100));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
queryDatabase();
updateRecords();
return null;
});
}
public void expensiveOperation() {
MethodProfiler.profile("expensiveOperation", () -> {
// Simulate expensive operation
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// CPU-intensive work
for (int i = 0; i < 100000; i++) {
String result = "result-" + i;
result = result.toUpperCase().toLowerCase();
}
return null;
});
}
private void validateData(String data) {
// Validation logic
}
private void transformData(String data) {
// Transformation logic
}
private void queryDatabase() {
// Database query logic
}
private void updateRecords() {
// Update logic
}
}
}
Key Features and Benefits
This comprehensive CPU profiler integration provides:
- Multiple Profiling Approaches: Sampling, instrumentation, and continuous profiling
- Low Overhead: Designed for production use with minimal performance impact
- Comprehensive Analysis: Hot spot identification, bottleneck detection, and optimization suggestions
- Integration Ready: Works with popular profilers like JFR, Async Profiler, and JProfiler
- Production Safe: Includes safeguards and configurable overhead limits
- Extensible: Easy to add custom metrics and profiling strategies
The integration helps developers identify performance bottlenecks, optimize critical code paths, and maintain application performance throughout the development lifecycle.