google-site-verification: google61fe8ba583a51912.html
Java Performance Tuning

Comprehensive guide to identifying, diagnosing, and fixing performance issues in Java applications.

1. Performance Monitoring and Profiling Tools

JVM Built-in Tools

// Performance monitoring utility class public class PerformanceMonitor { private static final Runtime runtime = Runtime.getRuntime(); public static void printMemoryUsage() { long maxMemory = runtime.maxMemory(); long totalMemory = runtime.totalMemory(); long freeMemory = runtime.freeMemory(); long usedMemory = totalMemory - freeMemory; System.out.printf("Memory Usage: Used=%dMB, Free=%dMB, Total=%dMB, Max=%dMB%n", bytesToMB(usedMemory), bytesToMB(freeMemory), bytesToMB(totalMemory), bytesToMB(maxMemory)); } public static void printGCStats() { for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) { System.out.printf("GC: %s - Count=%d, Time=%dms%n", gc.getName(), gc.getCollectionCount(), gc.getCollectionTime()); } } public static void printThreadStats() { ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); System.out.printf("Threads: Live=%d, Peak=%d, Daemon=%d%n", threadBean.getThreadCount(), threadBean.getPeakThreadCount(), threadBean.getDaemonThreadCount()); } public static void monitorMethodPerformance(Runnable task, String taskName) { long startTime = System.nanoTime(); long startMemory = runtime.totalMemory() - runtime.freeMemory(); task.run(); long endTime = System.nanoTime(); long endMemory = runtime.totalMemory() - runtime.freeMemory(); System.out.printf("Task: %s - Time=%dms, MemoryDelta=%d bytes%n", taskName, (endTime - startTime) / 1_000_000, (endMemory - startMemory)); } private static long bytesToMB(long bytes) { return bytes / (1024 * 1024); } }

Using JMX for Monitoring

// Custom JMX MBean for performance monitoring public interface PerformanceMBean { long getTotalRequests(); double getAverageResponseTime(); int getActiveConnections(); void resetStats(); } // MBean implementation public class PerformanceMonitorMBean implements PerformanceMBean { private final AtomicLong totalRequests = new AtomicLong(); private final AtomicLong totalResponseTime = new AtomicLong(); private final AtomicInteger activeConnections = new AtomicInteger(); @Override public long getTotalRequests() { return totalRequests.get(); } @Override public double getAverageResponseTime() { long requests = totalRequests.get(); return requests > 0 ? (double) totalResponseTime.get() / requests : 0; } @Override public int getActiveConnections() { return activeConnections.get(); } @Override public void resetStats() { totalRequests.set(0); totalResponseTime.set(0); } public void recordRequest(long responseTime) { totalRequests.incrementAndGet(); totalResponseTime.addAndGet(responseTime); } public void connectionOpened() { activeConnections.incrementAndGet(); } public void connectionClosed() { activeConnections.decrementAndGet(); } // Register MBean public static void registerMBean() { try { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); ObjectName name = new ObjectName("com.example:type=PerformanceMonitor"); PerformanceMonitorMBean mbean = new PerformanceMonitorMBean(); mbs.registerMBean(mbean, name); } catch (Exception e) { throw new RuntimeException("Failed to register MBean", e); } } }

2. Memory Optimization

Object Pooling

// Generic object pool implementation public class ObjectPool<T> { private final Supplier<T> creator; private final Consumer<T> resetter; private final Queue<T> pool; private final int maxSize; private final AtomicInteger createdCount = new AtomicInteger(); public ObjectPool(Supplier<T> creator, Consumer<T> resetter, int maxSize) { this.creator = creator; this.resetter = resetter; this.maxSize = maxSize; this.pool = new ConcurrentLinkedQueue<>(); } public T borrowObject() { T obj = pool.poll(); if (obj != null) { return obj; } if (createdCount.get() < maxSize) { createdCount.incrementAndGet(); return creator.get(); } // Wait for object to be returned or create new one try { // In real implementation, you might want to wait with timeout return creator.get(); } catch (Exception e) { throw new RuntimeException("Failed to create object", e); } } public void returnObject(T obj) { if (obj != null) { resetter.accept(obj); if (pool.size() < maxSize) { pool.offer(obj); } } } public int getPoolSize() { return pool.size(); } public int getCreatedCount() { return createdCount.get(); } } // Usage example with expensive objects public class ExpensiveObject { private byte[] data = new byte[1024 * 1024]; // 1MB private boolean initialized = false; public void initialize() { // Simulate expensive initialization Arrays.fill(data, (byte) 1); initialized = true; } public void reset() { Arrays.fill(data, (byte) 0); initialized = false; } public boolean process() { return initialized; } } // Pool usage public class ObjectPoolExample { private static final ObjectPool<ExpensiveObject> pool = new ObjectPool<>(ExpensiveObject::new, ExpensiveObject::reset, 10); public void processWithPool() { ExpensiveObject obj = pool.borrowObject(); try { obj.initialize(); obj.process(); } finally { pool.returnObject(obj); } } }

Memory-Efficient Collections

// Memory-optimized data structures public class MemoryEfficientCollections { // Primitive int list to avoid Integer boxing public static class IntList { private int[] data; private int size; public IntList(int initialCapacity) { this.data = new int[initialCapacity]; this.size = 0; } public void add(int value) { if (size == data.length) { data = Arrays.copyOf(data, data.length * 2); } data[size++] = value; } public int get(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException(); } return data[index]; } public int size() { return size; } public void trimToSize() { if (size < data.length) { data = Arrays.copyOf(data, size); } } } // Flyweight pattern for frequently used objects public static class StringFlyweight { private final Map<String, String> pool = new ConcurrentHashMap<>(); public String getCanonical(String str) { return pool.computeIfAbsent(str, k -> k); } public int getPoolSize() { return pool.size(); } } // Memory-efficient map for enum keys public static class EnumArrayMap<K extends Enum<K>, V> { private final V[] values; private final Class<K> keyType; @SuppressWarnings("unchecked") public EnumArrayMap(Class<K> keyType) { this.keyType = keyType; this.values = (V[]) new Object[keyType.getEnumConstants().length]; } public void put(K key, V value) { values[key.ordinal()] = value; } public V get(K key) { return values[key.ordinal()]; } public boolean containsKey(K key) { return values[key.ordinal()] != null; } } }

3. Garbage Collection Optimization

GC-Friendly Code Patterns

public class GarbageCollectionOptimization { // 1. Object reuse pattern public static class ObjectReuseExample { private final ThreadLocal<StringBuilder> threadLocalStringBuilder = ThreadLocal.withInitial(() -> new StringBuilder(1024)); public String buildMessage(String... parts) { StringBuilder sb = threadLocalStringBuilder.get(); sb.setLength(0); // Reset instead of creating new for (String part : parts) { sb.append(part); } return sb.toString(); } } // 2. Avoid large object allocation in loops public static class LoopOptimization { // BAD: Creates new StringBuilder in each iteration public void badLoop(List<String> items) { for (String item : items) { String result = new StringBuilder().append("Item: ").append(item).toString(); process(result); } } // GOOD: Reuse StringBuilder public void goodLoop(List<String> items) { StringBuilder sb = new StringBuilder(128); for (String item : items) { sb.setLength(0); sb.append("Item: ").append(item); process(sb.toString()); } } } // 3. Use primitive collections public static class PrimitiveCollectionExample { // BAD: Boxed integers public int sumBad(List<Integer> numbers) { int sum = 0; for (Integer num : numbers) { sum += num; // Auto-unboxing } return sum; } // GOOD: Primitive array public int sumGood(int[] numbers) { int sum = 0; for (int num : numbers) { sum += num; } return sum; } } // 4. Soft/Weak references for cache public static class MemorySensitiveCache<K, V> { private final Map<K, SoftReference<V>> cache = new ConcurrentHashMap<>(); private final Function<K, V> loader; public MemorySensitiveCache(Function<K, V> loader) { this.loader = loader; } public V get(K key) { SoftReference<V> ref = cache.get(key); V value = ref != null ? ref.get() : null; if (value == null) { value = loader.apply(key); cache.put(key, new SoftReference<>(value)); } return value; } public void cleanUp() { cache.entrySet().removeIf(entry -> entry.getValue().get() == null); } } }

GC Tuning Examples

// JVM Flag recommendations for different scenarios public class GCTuningGuide { /* * Throughput Optimized (Web Servers) * -XX:+UseG1GC -Xmx4g -Xms4g -XX:MaxGCPauseMillis=200 * -XX:ParallelGCThreads=4 -XX:ConcGCThreads=2 */ public static class ThroughputOptimized { // G1GC for predictable pause times } /* * Low Latency (Trading Systems) * -XX:+UseZGC -Xmx8g -Xms8g -XX:MaxGCPauseMillis=10 * -XX:ConcGCThreads=4 */ public static class LowLatency { // ZGC for sub-millisecond pauses } /* * Memory Constrained (Containers) * -XX:+UseSerialGC -Xmx512m -Xms512m * -XX:MaxMetaspaceSize=128m -XX:ReservedCodeCacheSize=64m */ public static class MemoryConstrained { // SerialGC for small heaps } } // Monitoring GC behavior public class GCMonitor { private static final Logger logger = Logger.getLogger(GCMonitor.class.getName()); public static void setupGCMonitoring() { List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); Timer timer = new Timer(true); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { for (GarbageCollectorMXBean gc : gcBeans) { long count = gc.getCollectionCount(); long time = gc.getCollectionTime(); if (count > 0) { logger.info(String.format("GC %s: Collections=%d, TotalTime=%dms, AvgTime=%.2fms", gc.getName(), count, time, (double) time / count)); } } // Log memory usage MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage(); logger.info(String.format("Heap: used=%dMB, committed=%dMB, max=%dMB", heapUsage.getUsed() / 1024 / 1024, heapUsage.getCommitted() / 1024 / 1024, heapUsage.getMax() / 1024 / 1024)); } }, 0, 60000); // Log every minute } }

4. CPU and Algorithm Optimization

Efficient Algorithms and Data Structures

public class AlgorithmOptimization { // 1. Use appropriate data structures public static class DataStructureSelection { // For frequent lookups by key public void hashMapExample() { Map<String, Integer> map = new HashMap<>(); // O(1) average case for get/put } // For range queries or sorted iteration public void treeMapExample() { Map<String, Integer> map = new TreeMap<>(); // O(log n) for get/put, maintains order } // For frequent insertions at both ends public void arrayDequeExample() { Deque<String> deque = new ArrayDeque<>(); // O(1) for add/remove at both ends } } // 2. Algorithm complexity optimization public static class AlgorithmComplexity { // O(n^2) - Bad for large datasets public boolean hasDuplicateBad(List<String> list) { for (int i = 0; i < list.size(); i++) { for (int j = i + 1; j < list.size(); j++) { if (list.get(i).equals(list.get(j))) { return true; } } } return false; } // O(n) - Good for large datasets public boolean hasDuplicateGood(List<String> list) { Set<String> seen = new HashSet<>(); for (String item : list) { if (!seen.add(item)) { return true; } } return false; } } // 3. Cache-friendly data access public static class CacheFriendly { // Bad: Non-contiguous memory access public void processLinkedList(LinkedList<Integer> list) { for (Integer num : list) { // Each access may cause cache miss process(num); } } // Good: Contiguous memory access public void processArrayList(ArrayList<Integer> list) { for (int i = 0; i < list.size(); i++) { // Sequential memory access - cache friendly process(list.get(i)); } } // Even better: Primitive array public void processArray(int[] array) { for (int i = 0; i < array.length; i++) { // Most cache-friendly process(array[i]); } } } // 4. Early termination and short-circuiting public static class EarlyTermination { public boolean containsSpecialBad(List<String> list) { boolean found = false; for (String item : list) { if (isSpecial(item)) { found = true; } } return found; } public boolean containsSpecialGood(List<String> list) { for (String item : list) { if (isSpecial(item)) { return true; // Early termination } } return false; } } // Helper methods private static void process(int num) { // Simulate processing } private static boolean isSpecial(String item) { return item.startsWith("special"); } }

JIT Optimization Techniques

public class JITOptimization { // 1. Method inlining friendly public static class InliningFriendly { // Small methods are more likely to be inlined public int calculate(int a, int b) { return add(multiply(a, b), subtract(a, b)); } private int add(int a, int b) { return a + b; // Likely to be inlined } private int multiply(int a, int b) { return a * b; // Likely to be inlined } private int subtract(int a, int b) { return a - b; // Likely to be inlined } } // 2. Avoid megamorphic call sites public static class PolymorphismOptimization { // Monomorphic (fastest) - single implementation public int processMonomorphic(Processor processor) { return processor.process(); // JIT can devirtualize } // Bimorphic (fast) - two implementations public int processBimorphic(Processor processor) { return processor.process(); // JIT can handle } // Megamorphic (slow) - many implementations public int processMegamorphic(Processor processor) { return processor.process(); // Virtual call needed } } // 3. Branch prediction friendly public static class BranchPrediction { // Data with predictable patterns public int sumEvenNumbers(int[] numbers) { int sum = 0; for (int num : numbers) { if (num % 2 == 0) { // Predictable pattern sum += num; } } return sum; } // Sort data to make branches predictable public void processSortedData(int[] data) { Arrays.sort(data); // Branches become more predictable for (int value : data) { if (value > 1000) { // Becomes predictable after sorting processHighValue(value); } } } } // 4. Loop optimizations public static class LoopOptimizations { // Loop unrolling public int sumArray(int[] array) { int sum = 0; int i = 0; // Unrolled loop - process 4 elements at a time for (; i <= array.length - 4; i += 4) { sum += array[i] + array[i + 1] + array[i + 2] + array[i + 3]; } // Process remaining elements for (; i < array.length; i++) { sum += array[i]; } return sum; } // Avoid method calls in hot loops public void optimizedLoop(List<String> items) { int size = items.size(); // Cache size for (int i = 0; i < size; i++) { // Avoid method call in condition String item = items.get(i); // Direct access processItem(item); } } } } interface Processor { int process(); } class SimpleProcessor implements Processor { @Override public int process() { return 1; } } class ComplexProcessor implements Processor { @Override public int process() { return 2; } }

5. I/O and Database Optimization

Efficient I/O Operations

public class IOOptimization { // 1. Use buffered I/O public static class BufferedIO { // Bad: Unbuffered I/O public void copyFileUnbuffered(Path source, Path target) throws IOException { try (InputStream is = Files.newInputStream(source); OutputStream os = Files.newOutputStream(target)) { int data; while ((data = is.read()) != -1) { // Single byte reads - very slow! os.write(data); } } } // Good: Buffered I/O public void copyFileBuffered(Path source, Path target) throws IOException { try (InputStream is = new BufferedInputStream(Files.newInputStream(source)); OutputStream os = new BufferedOutputStream(Files.newOutputStream(target))) { byte[] buffer = new byte[8192]; // 8KB buffer int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } } } } // 2. Use NIO for large files public static class NIOOptimization { public void copyFileNIO(Path source, Path target) throws IOException { try (FileChannel sourceChannel = FileChannel.open(source, StandardOpenOption.READ); FileChannel targetChannel = FileChannel.open(target, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) { long size = sourceChannel.size(); long position = 0; while (position < size) { position += sourceChannel.transferTo(position, 64 * 1024 * 1024, targetChannel); } } } } // 3. Connection pooling for databases public static class ConnectionPool { private final BlockingQueue<Connection> pool; private final AtomicInteger createdCount = new AtomicInteger(); private final int maxPoolSize; public ConnectionPool(int maxPoolSize) { this.maxPoolSize = maxPoolSize; this.pool = new LinkedBlockingQueue<>(maxPoolSize); } public Connection getConnection() throws SQLException { Connection conn = pool.poll(); if (conn != null && !conn.isClosed()) { return conn; } if (createdCount.get() < maxPoolSize) { createdCount.incrementAndGet(); return createNewConnection(); } try { return pool.take(); // Wait for available connection } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new SQLException("Interrupted while waiting for connection", e); } } public void returnConnection(Connection conn) { if (conn != null) { pool.offer(conn); } } private Connection createNewConnection() throws SQLException { // Implementation to create new database connection return null; } } // 4. Batch database operations public static class BatchOperations { public void insertUsersBad(List<User> users, Connection conn) throws SQLException { String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; try (PreparedStatement stmt = conn.prepareStatement(sql)) { for (User user : users) { stmt.setLong(1, user.getId()); stmt.setString(2, user.getName()); stmt.setString(3, user.getEmail()); stmt.executeUpdate(); // One round-trip per insert } } } public void insertUsersGood(List<User> users, Connection conn) throws SQLException { String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; try (PreparedStatement stmt = conn.prepareStatement(sql)) { for (User user : users) { stmt.setLong(1, user.getId()); stmt.setString(2, user.getName()); stmt.setString(3, user.getEmail()); stmt.addBatch(); // Add to batch // Execute in batches of 100 if (users.indexOf(user) % 100 == 0) { stmt.executeBatch(); } } stmt.executeBatch(); // Execute remaining } } } } class User { private long id; private String name; private String email; // constructor, getters, setters public User(long id, String name, String email) { this.id = id; this.name = name; this.email = email; } public long getId() { return id; } public String getName() { return name; } public String getEmail() { return email; } }

6. Concurrency Optimization

Efficient Concurrent Programming

public class ConcurrencyOptimization { // 1. Use appropriate concurrent collections public static class ConcurrentCollections { private final ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>(); private final CopyOnWriteArrayList<String> copyOnWriteList = new CopyOnWriteArrayList<>(); private final ConcurrentLinkedQueue<String> concurrentQueue = new ConcurrentLinkedQueue<>(); // Use ConcurrentHashMap for high-concurrency maps public void updateCounter(String key) { concurrentMap.compute(key, (k, v) -> v == null ? 1 : v + 1); } // Use CopyOnWriteArrayList for read-heavy, write-rarely scenarios public List<String> getSnapshot() { return new ArrayList<>(copyOnWriteList); } } // 2. Reduce lock contention public static class LockContentionReduction { // Bad: Single lock for all operations private final Object globalLock = new Object(); private int counter1 = 0; private int counter2 = 0; public void incrementBad() { synchronized (globalLock) { counter1++; counter2++; } } // Good: Separate locks for independent operations private final Object lock1 = new Object(); private final Object lock2 = new Object(); public void incrementGood() { synchronized (lock1) { counter1++; } synchronized (lock2) { counter2++; } } // Better: Use Atomic variables when possible private final AtomicInteger atomicCounter1 = new AtomicInteger(); private final AtomicInteger atomicCounter2 = new AtomicInteger(); public void incrementBest() { atomicCounter1.incrementAndGet(); atomicCounter2.incrementAndGet(); } } // 3. Thread pool optimization public static class ThreadPoolOptimization { // CPU-bound tasks public ExecutorService createCpuBoundPool() { int cores = Runtime.getRuntime().availableProcessors(); return Executors.newFixedThreadPool(cores); } // I/O bound tasks public ExecutorService createIoBoundPool() { return Executors.newCachedThreadPool(); } // Scheduled tasks public ScheduledExecutorService createScheduledPool() { return Executors.newScheduledThreadPool(4); } // Custom thread pool with monitoring public ThreadPoolExecutor createMonitoredPool(int coreSize, int maxSize) { return new ThreadPoolExecutor( coreSize, maxSize, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), new ThreadFactory() { private final AtomicInteger counter = new AtomicInteger(); @Override public Thread newThread(Runnable r) { Thread t = new Thread(r, "worker-" + counter.incrementAndGet()); t.setDaemon(true); return t; } }, new ThreadPoolExecutor.CallerRunsPolicy() // Handle rejection ) { @Override protected void beforeExecute(Thread t, Runnable r) { System.out.println("Starting task in thread: " + t.getName()); } @Override protected void afterExecute(Runnable r, Throwable t) { if (t != null) { System.err.println("Task failed: " + t.getMessage()); } } }; } } // 4. Non-blocking algorithms public static class NonBlockingCounter { private final AtomicLong counter = new AtomicLong(); private final LongAdder highPerformanceCounter = new LongAdder(); // Simple atomic counter public long incrementAndGet() { return counter.incrementAndGet(); } // High-performance counter for high contention public long incrementAndGetHighPerf() { highPerformanceCounter.increment(); return highPerformanceCounter.sum(); } } // 5. CompletableFuture for async programming public static class AsyncOptimization { public CompletableFuture<String> processAsync(String input) { return CompletableFuture.supplyAsync(() -> expensiveOperation(input)) .thenApplyAsync(this::transformResult) .exceptionally(this::handleError); } public CompletableFuture<Void> processMultipleAsync(List<String> inputs) { List<CompletableFuture<String>> futures = inputs.stream() .map(this::processAsync) .toList(); return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); } private String expensiveOperation(String input) { // Simulate expensive operation try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return input.toUpperCase(); } private String transformResult(String result) { return "Processed: " + result; } private String handleError(Throwable throwable) { return "Error: " + throwable.getMessage(); } } }

7. JVM Tuning and Configuration

JVM Flag Optimization

// JVM tuning configurations for different scenarios public class JVMTuningConfigs { /* * Web Application Server (Spring Boot, Tomcat) */ public static class WebAppTuning { // Recommended JVM flags: String[] flags = { "-server", // Server VM "-Xmx4g", "-Xms4g", // Fixed heap size "-XX:+UseG1GC", // G1 Garbage Collector "-XX:MaxGCPauseMillis=200", // Target max pause time "-XX:ParallelGCThreads=4", // Parallel GC threads "-XX:ConcGCThreads=2", // Concurrent GC threads "-XX:+AlwaysPreTouch", // Pre-touch memory pages "-XX:+UseStringDeduplication", // Deduplicate strings "-XX:+UseCompressedOops", // Compress object pointers "-XX:MetaspaceSize=256m", // Metaspace initial size "-XX:MaxMetaspaceSize=512m", // Metaspace max size "-Djava.security.egd=file:/dev/./urandom" // Faster random }; } /* * High-Performance Computing */ public static class HighPerformanceTuning { // Recommended JVM flags: String[] flags = { "-server", "-Xmx16g", "-Xms16g", "-XX:+UseZGC", // ZGC for low latency "-XX:MaxGCPauseMillis=10", // Very low pause target "-XX:+UseLargePages", // Use large memory pages "-XX:+UseTransparentHugePages", "-XX:+UseNUMA", // NUMA awareness "-XX:+AggressiveOpts", // Aggressive optimizations "-XX:+UseBiasedLocking", // Biased locking "-XX:ReservedCodeCacheSize=512m", // Larger code cache "-XX:InitialCodeCacheSize=256m" }; } /* * Microservices / Containerized */ public static class ContainerTuning { // Recommended JVM flags: String[] flags = { "-XX:+UseContainerSupport", // Container awareness "-XX:MaxRAMPercentage=75.0", // Use 75% of container memory "-XX:InitialRAMPercentage=50.0", // Initial memory percentage "-XX:MinRAMPercentage=25.0", // Minimum memory percentage "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=100", "-XX:+ExitOnOutOfMemoryError", // Exit on OOM "-XX:+CrashOnOutOfMemoryError", // Crash on OOM "-XX:NativeMemoryTracking=detail", // NMT for memory analysis "-Xlog:gc*:file=/logs/gc.log:time" // GC logging }; } } // Runtime optimization based on environment public class AdaptiveOptimization { private static final boolean IS_CONTAINER = System.getenv("KUBERNETES_SERVICE_HOST") != null; private static final int AVAILABLE_CORES = Runtime.getRuntime().availableProcessors(); private static final long MAX_MEMORY = Runtime.getRuntime().maxMemory(); public static void applyOptimalSettings() { if (IS_CONTAINER) { applyContainerSettings(); } else { applyPhysicalServerSettings(); } // Log current JVM settings logJVMSettings(); } private static void applyContainerSettings() { System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", String.valueOf(Math.max(2, AVAILABLE_CORES / 2))); } private static void applyPhysicalServerSettings() { System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", String.valueOf(AVAILABLE_CORES)); } private static void logJVMSettings() { System.out.println("JVM Settings:"); System.out.println(" Cores: " + AVAILABLE_CORES); System.out.println(" Max Memory: " + (MAX_MEMORY / 1024 / 1024) + "MB"); System.out.println(" Common Parallelism: " + System.getProperty("java.util.concurrent.ForkJoinPool.common.parallelism")); System.out.println(" GC: " + ManagementFactory.getGarbageCollectorMXBeans().stream() .map(GarbageCollectorMXBean::getName) .collect(Collectors.joining(", "))); } }

Performance Testing Framework

// Simple performance testing utility public class PerformanceTest { private final String testName; private final int warmupIterations; private final int measurementIterations; public PerformanceTest(String testName, int warmupIterations, int measurementIterations) { this.testName = testName; this.warmupIterations = warmupIterations; this.measurementIterations = measurementIterations; } public TestResult runTest(Runnable testCode) { // Warmup phase for (int i = 0; i < warmupIterations; i++) { testCode.run(); } // Measurement phase long totalTime = 0; long minTime = Long.MAX_VALUE; long maxTime = Long.MIN_VALUE; for (int i = 0; i < measurementIterations; i++) { long startTime = System.nanoTime(); testCode.run(); long endTime = System.nanoTime(); long duration = endTime - startTime; totalTime += duration; minTime = Math.min(minTime, duration); maxTime = Math.max(maxTime, duration); } long avgTime = totalTime / measurementIterations; return new TestResult(testName, avgTime, minTime, maxTime, warmupIterations, measurementIterations); } public static class TestResult { private final String testName; private final long averageTimeNs; private final long minTimeNs; private final long maxTimeNs; private final int warmupIterations; private final int measurementIterations; public TestResult(String testName, long averageTimeNs, long minTimeNs, long maxTimeNs, int warmupIterations, int measurementIterations) { this.testName = testName; this.averageTimeNs = averageTimeNs; this.minTimeNs = minTimeNs; this.maxTimeNs = maxTimeNs; this.warmupIterations = warmupIterations; this.measurementIterations = measurementIterations; } @Override public String toString() { return String.format( "Test: %s\n" + " Iterations: %d warmup, %d measurement\n" + " Average: %.3f ms\n" + " Min: %.3f ms\n" + " Max: %.3f ms\n" + " Throughput: %.2f ops/sec", testName, warmupIterations, measurementIterations, averageTimeNs / 1_000_000.0, minTimeNs / 1_000_000.0, maxTimeNs / 1_000_000.0, 1_000_000_000.0 / averageTimeNs ); } } } // Usage example public class PerformanceTestExample { public static void main(String[] args) { PerformanceTest test = new PerformanceTest("String Concatenation", 1000, 10000); TestResult result1 = test.runTest(() -> { // Test method 1 String result = ""; for (int i = 0; i < 100; i++) { result += "test" + i; } }); TestResult result2 = test.runTest(() -> { // Test method 2 StringBuilder sb = new StringBuilder(); for (int i = 0; i < 100; i++) { sb.append("test").append(i); } String result = sb.toString(); }); System.out.println(result1); System.out.println(result2); } }

Key Performance Tuning Principles:

  1. Measure First: Always profile before optimizing
  2. Memory Efficiency: Reduce object creation, use appropriate data structures
  3. Algorithm Selection: Choose algorithms with better complexity
  4. Concurrency: Use appropriate synchronization and thread pools
  5. I/O Optimization: Use buffering, batching, and async operations
  6. JVM Tuning: Configure GC and JVM settings for your workload
  7. Cache-Friendly: Optimize data access patterns for CPU cache
  8. Avoid Premature Optimization: Focus on bottlenecks that actually matter

This comprehensive guide covers the essential aspects of Java performance tuning, from basic monitoring to advanced optimization techniques.

Leave a Reply

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


Macro Nepal Helper