Performance Tuning

1. JVM Tuning

Memory Configuration

# Heap Size Configuration
-Xms2g                  # Initial Heap Size
-Xmx4g                  # Maximum Heap Size
-XX:MetaspaceSize=256m # Initial Metaspace Size
-XX:MaxMetaspaceSize=512m # Maximum Metaspace Size

# Garbage Collection
-XX:+UseG1GC           # Use G1 Garbage Collector
-XX:MaxGCPauseMillis=200 # Target GC Pause Time
-XX:ParallelGCThreads=4 # Number of GC Threads
-XX:ConcGCThreads=2    # Number of Concurrent GC Threads

# Memory Management
-XX:+HeapDumpOnOutOfMemoryError # Create Heap Dump on OOM
-XX:HeapDumpPath=/var/log/heap-dump.hprof # Heap Dump Location

JVM Monitoring

@Configuration
public class JvmMonitoringConfig {

    @Bean
    public MeterRegistry jvmMetrics() {
        return new JvmMetricsRegistry();
    }

    @Bean
    public JvmGcMetrics gcMetrics() {
        return new JvmGcMetrics();
    }

    @Bean
    public JvmMemoryMetrics memoryMetrics() {
        return new JvmMemoryMetrics();
    }

    @Bean
    public JvmThreadMetrics threadMetrics() {
        return new JvmThreadMetrics();
    }
}

@Component
public class JvmHealthIndicator implements HealthIndicator {

    private final Runtime runtime = Runtime.getRuntime();

    @Override
    public Health health() {
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long maxMemory = runtime.maxMemory();

        return Health.up()
            .withDetail("total_memory", formatSize(totalMemory))
            .withDetail("free_memory", formatSize(freeMemory))
            .withDetail("max_memory", formatSize(maxMemory))
            .withDetail("processors", runtime.availableProcessors())
            .build();
    }

    private String formatSize(long bytes) {
        return bytes / (1024 * 1024) + "MB";
    }
}

2. Thread Pool Optimization

Executor Configuration

@Configuration
public class ThreadPoolConfig {

    @Bean
    public ThreadPoolTaskExecutor applicationTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(100);
        executor.setKeepAliveSeconds(120);
        executor.setThreadNamePrefix("app-task-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // Monitor thread pool metrics
        executor.setThreadFactory(new MonitoredThreadFactory());

        return executor;
    }
}

public class MonitoredThreadFactory implements ThreadFactory {

    private final ThreadFactory delegate = Executors.defaultThreadFactory();
    private final MeterRegistry registry;

    public Thread newThread(Runnable r) {
        Thread thread = delegate.newThread(r);

        registry.gauge("thread.pool.active",
            Tags.of("name", thread.getName()),
            thread,
            t -> t.isAlive() ? 1 : 0);

        return thread;
    }
}

Async Processing

@Service
public class AsyncProcessingService {

    private final ThreadPoolTaskExecutor executor;
    private final MeterRegistry registry;

    @Async
    public CompletableFuture<ProcessingResult> processAsync(Task task) {
        Timer.Sample sample = Timer.start(registry);

        try {
            ProcessingResult result = processTask(task);

            sample.stop(Timer.builder("async.processing")
                .tag("task", task.getType())
                .register(registry));

            return CompletableFuture.completedFuture(result);
        } catch (Exception e) {
            registry.counter("async.errors",
                "task", task.getType()).increment();
            throw e;
        }
    }

    @Scheduled(fixedRate = 60000)
    public void monitorThreadPool() {
        ThreadPoolExecutor threadPool = executor.getThreadPoolExecutor();

        registry.gauge("thread.pool.size",
            threadPool.getPoolSize());
        registry.gauge("thread.pool.active",
            threadPool.getActiveCount());
        registry.gauge("thread.pool.queue",
            threadPool.getQueue().size());
    }
}

3. Database Connection Pool

HikariCP Optimization

@Configuration
public class DatabaseConfig {

    @Bean
    public HikariConfig hikariConfig() {
        HikariConfig config = new HikariConfig();

        // Basic Configuration
        config.setDriverClassName("org.postgresql.Driver");
        config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
        config.setUsername("user");
        config.setPassword("password");

        // Pool Configuration
        config.setMaximumPoolSize(20);
        config.setMinimumIdle(5);
        config.setIdleTimeout(300000); // 5 minutes
        config.setMaxLifetime(1800000); // 30 minutes
        config.setConnectionTimeout(30000); // 30 seconds

        // Performance Optimization
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        config.addDataSourceProperty("useServerPrepStmts", "true");

        return config;
    }

    @Bean
    public DataSource dataSource() {
        return new HikariDataSource(hikariConfig());
    }
}

Connection Pool Monitoring

@Component
public class ConnectionPoolMetrics {

    private final HikariDataSource dataSource;
    private final MeterRegistry registry;

    @Scheduled(fixedRate = 10000)
    public void recordMetrics() {
        HikariPoolMXBean poolMXBean = dataSource.getHikariPoolMXBean();

        registry.gauge("hikari.connections.active",
            poolMXBean.getActiveConnections());
        registry.gauge("hikari.connections.idle",
            poolMXBean.getIdleConnections());
        registry.gauge("hikari.connections.total",
            poolMXBean.getTotalConnections());
        registry.gauge("hikari.connections.pending",
            poolMXBean.getThreadsAwaitingConnection());
    }

    @EventListener
    public void onPoolStatsChange(PoolStatsEvent event) {
        logger.info("Connection pool stats: active={}, idle={}, waiting={}",
            event.getActiveConnections(),
            event.getIdleConnections(),
            event.getThreadsAwaitingConnection());
    }
}

4. Caching Strategy

Multi-Level Cache

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        return new LayeredCacheManager(
            localCache(),
            redisCache()
        );
    }

    @Bean
    public CacheManager localCache() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();

        cacheManager.setCaffeine(Caffeine.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(Duration.ofMinutes(5))
            .recordStats());

        return cacheManager;
    }

    @Bean
    public CacheManager redisCache() {
        RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory())
            .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                    .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                    .fromSerializer(new GenericJackson2JsonRedisSerializer())))
            .build();

        return cacheManager;
    }
}

Cache Monitoring

@Component
public class CacheMonitor {

    private final MeterRegistry registry;
    private final CacheManager cacheManager;

    @Scheduled(fixedRate = 60000)
    public void recordMetrics() {
        cacheManager.getCacheNames().forEach(cacheName -> {
            Cache cache = cacheManager.getCache(cacheName);
            CacheStats stats = ((CaffeineCache) cache).getNativeCache().stats();

            registry.gauge("cache.size",
                Tags.of("name", cacheName),
                cache,
                this::getCacheSize);

            registry.gauge("cache.hit.ratio",
                Tags.of("name", cacheName),
                stats.hitRate());

            registry.gauge("cache.miss.ratio",
                Tags.of("name", cacheName),
                stats.missRate());
        });
    }

    private long getCacheSize(Cache cache) {
        if (cache instanceof CaffeineCache) {
            return ((CaffeineCache) cache).getNativeCache().estimatedSize();
        }
        return 0;
    }
}

5. Best Practices

1. Resource Management

@Component
public class ResourceManager {

    private final ExecutorService executor;
    private final DataSource dataSource;

    @PreDestroy
    public void cleanup() {
        // Graceful shutdown of thread pools
        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }

        // Close database connections
        if (dataSource instanceof HikariDataSource) {
            ((HikariDataSource) dataSource).close();
        }
    }

    public void monitorResources() {
        // Monitor thread usage
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        long[] threadIds = threadBean.getAllThreadIds();

        registry.gauge("threads.total", threadIds.length);
        registry.gauge("threads.blocked",
            Arrays.stream(threadBean.getThreadInfo(threadIds))
                .filter(info -> info.getThreadState() == Thread.State.BLOCKED)
                .count());

        // Monitor memory usage
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();

        registry.gauge("memory.used", heapUsage.getUsed());
        registry.gauge("memory.committed", heapUsage.getCommitted());
        registry.gauge("memory.max", heapUsage.getMax());
    }
}

2. Response Time Optimization

@Aspect
@Component
public class PerformanceMonitor {

    private final MeterRegistry registry;
    private final Logger logger = LoggerFactory.getLogger(PerformanceMonitor.class);

    @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public Object monitorEndpoint(ProceedingJoinPoint joinPoint) throws Throwable {
        Timer.Sample sample = Timer.start(registry);
        String endpoint = joinPoint.getSignature().toShortString();

        try {
            Object result = joinPoint.proceed();

            sample.stop(Timer.builder("http.server.requests")
                .tag("endpoint", endpoint)
                .register(registry));

            return result;
        } catch (Exception e) {
            registry.counter("http.server.errors",
                "endpoint", endpoint,
                "error", e.getClass().getSimpleName()
            ).increment();

            throw e;
        }
    }

    @Scheduled(fixedRate = 60000)
    public void checkSlowEndpoints() {
        registry.find("http.server.requests")
            .timer()
            .ifPresent(timer -> {
                if (timer.mean(TimeUnit.MILLISECONDS) > 1000) {
                    logger.warn("Slow endpoint detected: {}",
                        timer.getId().getTag("endpoint"));
                }
            });
    }
}

3. Load Testing

@SpringBootTest
public class PerformanceTest {

    @Autowired
    private WebTestClient webClient;

    @Test
    public void loadTest() {
        int users = 100;
        int requestsPerUser = 50;

        Flux.range(1, users)
            .flatMap(user ->
                Flux.range(1, requestsPerUser)
                    .flatMap(request ->
                        webClient.get()
                            .uri("/api/products")
                            .exchange()
                            .doOnNext(response ->
                                assertTrue(response.statusCode().is2xxSuccessful()))
                    )
            )
            .blockLast(Duration.ofMinutes(5));
    }

    @Test
    public void stressTest() {
        StressTestBuilder.builder()
            .withConcurrentUsers(100)
            .withRampUpPeriod(Duration.ofSeconds(30))
            .withHoldPeriod(Duration.ofMinutes(5))
            .withTestCase(() ->
                webClient.get()
                    .uri("/api/products")
                    .exchange()
                    .block()
            )
            .withAssertions(results -> {
                assertTrue(results.getErrorRate() < 0.01);
                assertTrue(results.getP95ResponseTime()
                    .compareTo(Duration.ofSeconds(1)) < 0);
            })
            .build()
            .execute();
    }
}

4. Memory Optimization

@Service
public class MemoryOptimizationService {

    private final LoadingCache<String, byte[]> cache;

    public MemoryOptimizationService() {
        this.cache = Caffeine.newBuilder()
            .maximumWeight(100_000_000) // 100MB
            .weigher((String key, byte[] value) -> value.length)
            .build(this::loadData);
    }

    public void processLargeData(InputStream input) {
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(input))) {

            String line;
            StringBuilder buffer = new StringBuilder(8192);

            while ((line = reader.readLine()) != null) {
                if (buffer.length() + line.length() > 8192) {
                    processBuffer(buffer);
                    buffer.setLength(0);
                }
                buffer.append(line);
            }

            if (buffer.length() > 0) {
                processBuffer(buffer);
            }
        }
    }

    @Scheduled(fixedRate = 3600000)
    public void cleanupMemory() {
        System.gc();
        Runtime runtime = Runtime.getRuntime();

        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long usedMemory = totalMemory - freeMemory;

        logger.info("Memory usage: total={}, free={}, used={}",
            formatSize(totalMemory),
            formatSize(freeMemory),
            formatSize(usedMemory));
    }
}

5. Monitoring and Alerting

```java @Configuration public class MonitoringConfig {

@Bean
public MeterRegistry meterRegistry() {
    CompositeMeterRegistry registry = new CompositeMeterRegistry();

    registry.add(new SimpleMeterRegistry());
    registry.add(new PrometheusRegistry());

    registry.config()
        .commonTags("application", "myapp")
        .meterFilter(MeterFilter.deny(id ->
            id.getName().startsWith("jvm")));

    return registry;
}

}

@Component public class AlertingService {

private final MeterRegistry registry;
private final AlertNotifier notifier;

@Scheduled(fixedRate = 60000)
public void checkMetrics() {
    // Check CPU usage
    registry.find("system.cpu.usage")
        .gauge()
        .ifPresent(gauge -> {
            if (gauge.value() > 0.8) { // 80%
                notifier.sendAlert(
                    "High CPU Usage",
                    String.format("CPU usage is at %.2f%%",
                        gauge.value() * 100));
            }
        });

    // Check memory usage
    registry.find("jvm.memory.used")
        .gauge()
        .ifPresent(gauge -> {
            double usedMemory = gauge.value() / (1024 * 1024); // MB
            if (usedMemory > 1000) { // 1GB
                notifier.sendAlert(
                    "High Memory Usage",
                    String.format("Memory usage is at %.2f MB",
                        usedMemory));
            }
        });

    // Check error rate
    registry.find("http.server.errors")
        .counter()
        .ifPresent(counter -> {
            double errorRate = counter.count() /
                registry.find("http.server.requests")
                    .timer()
                    .map(Timer::count)
                    .orElse(1.0);

            if (errorRate > 0.05) { // 5%
                notifier.sendAlert(
                    "High Error Rate",
                    String.format("Error rate is at %.2f%%",
                        errorRate * 100));
            }
        });
}

}