Performance tuning is crucial for optimizing Java applications. This comprehensive guide covers essential techniques, tools, and best practices for improving Java application performance.
Key areas covered:
// Using WeakHashMap to prevent memory leaks
public class CacheManager {
private final WeakHashMap cache =
new WeakHashMap<>();
public void cacheData(String key, byte[] data) {
cache.put(key, data);
}
// Using try-with-resources for proper resource cleanup
public void processLargeFile(String filePath) {
try (FileInputStream fis = new FileInputStream(filePath);
BufferedInputStream bis = new BufferedInputStream(fis)) {
// Process file
} catch (IOException e) {
logger.error("Error processing file: " + e.getMessage());
}
}
}
// Memory leak prevention in custom collections
public class CustomCache {
private final int maxSize;
private final Map cache;
public CustomCache(int maxSize) {
this.maxSize = maxSize;
this.cache = new LinkedHashMap(maxSize, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > maxSize;
}
};
}
}
public class ObjectPool {
private final Queue pool;
private final Supplier factory;
private final int maxSize;
public ObjectPool(Supplier factory, int maxSize) {
this.factory = factory;
this.maxSize = maxSize;
this.pool = new ConcurrentLinkedQueue<>();
}
public T borrow() {
T instance = pool.poll();
return instance != null ? instance : factory.get();
}
public void release(T instance) {
if (pool.size() < maxSize) {
pool.offer(instance);
}
}
// Usage example
public static void main(String[] args) {
ObjectPool pool =
new ObjectPool<>(StringBuilder::new, 100);
StringBuilder sb = pool.borrow();
try {
sb.append("Hello").append(" World");
// Use StringBuilder
} finally {
sb.setLength(0); // Reset
pool.release(sb);
}
}
}
# G1GC Configuration
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16M
-XX:G1ReservePercent=10
-XX:InitiatingHeapOccupancyPercent=45
# Memory Settings
-Xms4g
-Xmx4g
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
# GC Logging
-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dumps
# Thread Stack Size
-Xss512k
# Large Pages
-XX:+UseLargePages
-XX:LargePageSizeInBytes=2m
// JMX Monitoring Configuration
@Configuration
public class JmxConfig {
@Bean
public MBeanExporter mBeanExporter() {
MBeanExporter exporter = new MBeanExporter();
Map beans = new HashMap<>();
beans.put("bean:name=performanceMonitor",
new PerformanceMonitorMBean());
exporter.setBeans(beans);
return exporter;
}
}
// Custom Performance MBean
public class PerformanceMonitorMBean implements PerformanceMonitorMXBean {
private final Map timers = new ConcurrentHashMap<>();
@Override
public void startTimer(String name) {
timers.computeIfAbsent(name, k -> new Timer()).start();
}
@Override
public long stopTimer(String name) {
Timer timer = timers.get(name);
return timer != null ? timer.stop() : -1;
}
@Override
public Map getTimings() {
return timers.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().getElapsedTime()
));
}
}
// String concatenation optimization
public class StringOptimization {
// Bad practice
public String concatenateStrings(List strings) {
String result = "";
for (String s : strings) {
result += s; // Creates new String object each time
}
return result;
}
// Good practice
public String concatenateStringsOptimized(List strings) {
StringBuilder sb = new StringBuilder(strings.size() * 16);
for (String s : strings) {
sb.append(s);
}
return sb.toString();
}
// String interning
public void stringInternExample() {
String s1 = new String("hello").intern();
String s2 = "hello";
assert s1 == s2; // true after interning
}
// String formatting
public String formatString(String name, int age) {
// Bad practice
return "Name: " + name + ", Age: " + age;
// Good practice
return String.format("Name: %s, Age: %d", name, age);
// Better practice for frequent use
return new StringBuilder(32)
.append("Name: ")
.append(name)
.append(", Age: ")
.append(age)
.toString();
}
}
public class CollectionOptimization {
// Initialize with proper size
public List createList(int size) {
return new ArrayList<>(size); // Prevents resizing
}
// Use proper collection type
public Set createSet(boolean ordered) {
return ordered ? new LinkedHashSet<>() : new HashSet<>();
}
// Bulk operations
public void bulkOperations(List source) {
List destination = new ArrayList<>(source.size());
destination.addAll(source); // Better than individual adds
// Use removeIf instead of iterator
destination.removeIf(s -> s.isEmpty());
}
// Custom sorting
public void sortList(List people) {
// Create comparator once
Comparator comparator = Comparator
.comparing(Person::getLastName)
.thenComparing(Person::getFirstName);
people.sort(comparator);
}
// Use streams efficiently
public List processStrings(List input) {
return input.stream()
.filter(s -> !s.isEmpty())
.map(String::trim)
.distinct()
.collect(Collectors.toCollection(
() -> new ArrayList<>(input.size())
));
}
}
// HikariCP Configuration
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
config.setUsername("user");
config.setPassword("password");
// Pool settings
config.setMaximumPoolSize(10);
config.setMinimumIdle(5);
config.setIdleTimeout(300000);
config.setConnectionTimeout(20000);
config.setMaxLifetime(1200000);
// Performance settings
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
return new HikariDataSource(config);
}
}
@Service
@Transactional
public class BatchProcessor {
private final JdbcTemplate jdbcTemplate;
private final int batchSize = 1000;
public void batchInsert(List people) {
String sql = "INSERT INTO person (first_name, last_name, age) " +
"VALUES (?, ?, ?)";
jdbcTemplate.batchUpdate(sql,
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i)
throws SQLException {
Person person = people.get(i);
ps.setString(1, person.getFirstName());
ps.setString(2, person.getLastName());
ps.setInt(3, person.getAge());
}
@Override
public int getBatchSize() {
return people.size();
}
});
}
public void processLargeDataset(Stream people) {
List batch = new ArrayList<>(batchSize);
people.forEach(person -> {
batch.add(person);
if (batch.size() >= batchSize) {
batchInsert(batch);
batch.clear();
}
});
if (!batch.isEmpty()) {
batchInsert(batch);
}
}
}
@Configuration
public class ThreadPoolConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// Core pool size
executor.setCorePoolSize(Runtime.getRuntime()
.availableProcessors());
// Maximum pool size
executor.setMaxPoolSize(Runtime.getRuntime()
.availableProcessors() * 2);
// Queue capacity
executor.setQueueCapacity(500);
// Thread naming
executor.setThreadNamePrefix("async-");
// Rejection policy
executor.setRejectedExecutionHandler(
new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
@Service
public class AsyncService {
private final Executor executor;
public CompletableFuture processAsync(Request request) {
return CompletableFuture.supplyAsync(() -> {
// Process request
return new Result();
}, executor);
}
public void processBatch(List requests) {
List> futures = requests.stream()
.map(this::processAsync)
.collect(Collectors.toList());
CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0]))
.join();
}
}
public class LockOptimization {
// Use ReentrantLock for complex scenarios
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void complexOperation() {
lock.lock();
try {
// Critical section
while (!readyToProcess()) {
condition.await();
}
process();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
// Use ReadWriteLock for read-heavy scenarios
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
public void readOperation() {
readLock.lock();
try {
// Read operation
} finally {
readLock.unlock();
}
}
public void writeOperation() {
writeLock.lock();
try {
// Write operation
} finally {
writeLock.unlock();
}
}
}
# JFR Recording Options
-XX:StartFlightRecording=duration=60s,filename=recording.jfr,
settings=profile
# Custom JFR Event
@Label("Custom Performance Event")
@Name("com.example.CustomEvent")
@Category("Performance")
@Description("Custom performance monitoring event")
public class CustomEvent extends Event {
@Label("Operation Name")
private final String operation;
@Label("Duration (ms)")
private final long duration;
public CustomEvent(String operation, long duration) {
this.operation = operation;
this.duration = duration;
}
public void record() {
if (isEnabled()) {
commit();
}
}
}
@Configuration
public class MetricsConfig {
@Bean
public MeterRegistry meterRegistry() {
return new SimpleMeterRegistry();
}
}
@Service
public class PerformanceMonitor {
private final MeterRegistry registry;
private final Map timers = new ConcurrentHashMap<>();
public void recordOperation(String name, Runnable operation) {
Timer timer = timers.computeIfAbsent(name,
k -> Timer.builder(k)
.description("Operation timing")
.register(registry));
timer.record(operation);
}
public void recordValue(String name, double value) {
Gauge.builder(name, () -> value)
.description("Custom metric")
.register(registry);
}
public void countEvent(String name) {
Counter counter = Counter.builder(name)
.description("Event counter")
.register(registry);
counter.increment();
}
}
Performance tuning is an essential aspect of Java application development. By following the techniques and best practices outlined in this guide, you can significantly improve your application's performance and resource utilization.
Remember to always measure and profile before optimizing, and focus on the areas that will provide the most significant performance improvements for your specific use case.