Java Performance Tips¶
This guide covers Java performance optimization techniques, best practices, and tools for writing efficient Java applications.
🎯 Core Performance Principles¶
JVM Understanding¶
- Garbage Collection: Understand GC algorithms and tuning
- Memory Management: Heap vs. stack, object lifecycle
- JIT Compilation: HotSpot optimization and warm-up
- Class Loading: Impact on startup performance
Measurement First¶
- Profile Before Optimizing: Use profilers to identify bottlenecks
- Benchmark Properly: Use JMH for microbenchmarks
- Monitor Production: Use APM tools for real-world performance
- Set Performance Goals: Define measurable performance targets
🔧 Memory Optimization¶
Object Creation¶
// Bad: Creating many temporary objects
public String concatenateBad(List<String> items) {
String result = "";
for (String item : items) {
result += item; // Creates new String each iteration
}
return result;
}
// Good: Use StringBuilder
public String concatenateGood(List<String> items) {
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item);
}
return sb.toString();
}
// Better: Use String.join (Java 8+)
public String concatenateBest(List<String> items) {
return String.join("", items);
}
Collection Usage¶
// Bad: Using wrong collection type
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
numbers.add(0, i); // O(n) operation - very slow!
}
// Good: Use appropriate collection
List<Integer> numbers = new LinkedList<>();
for (int i = 0; i < 1000000; i++) {
numbers.add(0, i); // O(1) operation
}
// Better: Use ArrayList and add at end
List<Integer> numbers = new ArrayList<>(1000000);
for (int i = 0; i < 1000000; i++) {
numbers.add(i); // O(1) amortized
}
Memory Leaks Prevention¶
// Bad: Static references to objects
public class MemoryLeak {
private static List<Object> cache = new ArrayList<>();
public void addToCache(Object obj) {
cache.add(obj); // Objects never garbage collected!
}
}
// Good: Use weak references for caches
public class GoodCache {
private static Map<String, WeakReference<Object>> cache = new HashMap<>();
public void addToCache(String key, Object obj) {
cache.put(key, new WeakReference<>(obj));
}
public Object getFromCache(String key) {
WeakReference<Object> ref = cache.get(key);
return ref != null ? ref.get() : null;
}
}
🚀 Algorithm Optimization¶
Loop Optimization¶
// Bad: Inefficient loop
public int findMax(int[] array) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
}
// Good: Enhanced for-loop (cleaner, similar performance)
public int findMaxEnhanced(int[] array) {
int max = Integer.MIN_VALUE;
for (int value : array) {
if (value > max) {
max = value;
}
}
return max;
}
// Better: Use streams (Java 8+)
public int findMaxStream(int[] array) {
return Arrays.stream(array).max().orElse(Integer.MIN_VALUE);
}
String Operations¶
// Bad: Repeated string operations
public boolean containsKeywords(String text, List<String> keywords) {
for (String keyword : keywords) {
if (text.toLowerCase().contains(keyword.toLowerCase())) {
return true;
}
}
return false;
}
// Good: Compile patterns once
public class KeywordMatcher {
private final List<Pattern> patterns;
public KeywordMatcher(List<String> keywords) {
this.patterns = keywords.stream()
.map(keyword -> Pattern.compile(Pattern.quote(keyword), Pattern.CASE_INSENSITIVE))
.collect(Collectors.toList());
}
public boolean containsKeywords(String text) {
return patterns.stream().anyMatch(pattern -> pattern.matcher(text).find());
}
}
📊 Concurrency Performance¶
Thread Pool Usage¶
// Bad: Creating threads manually
public void processTasks(List<Runnable> tasks) {
for (Runnable task : tasks) {
new Thread(task).start(); // Creates unlimited threads!
}
}
// Good: Use thread pool
public void processTasksGood(List<Runnable> tasks) {
ExecutorService executor = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors()
);
for (Runnable task : tasks) {
executor.submit(task);
}
executor.shutdown();
try {
executor.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// Better: Use parallel streams (Java 8+)
public void processTasksParallel(List<Runnable> tasks) {
tasks.parallelStream().forEach(Runnable::run);
}
Synchronization¶
// Bad: Excessive synchronization
public class SynchronizedCounter {
private int count = 0;
public synchronized void increment() {
count++; // Synchronized even for single-threaded use
}
public synchronized int getCount() {
return count;
}
}
// Good: Use atomic classes for simple operations
public class AtomicCounter {
private final AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // Lock-free operation
}
public int getCount() {
return count.get();
}
}
// Better: Use LongAdder for high contention
public class HighPerformanceCounter {
private final LongAdder count = new LongAdder();
public void increment() {
count.increment(); // Better under high contention
}
public long getCount() {
return count.sum();
}
}
🛠️ I/O Performance¶
File Operations¶
// Bad: Reading file byte by byte
public String readFileBad(String filename) throws IOException {
FileInputStream fis = new FileInputStream(filename);
String content = "";
int byteRead;
while ((byteRead = fis.read()) != -1) {
content += (char) byteRead; // Very slow!
}
fis.close();
return content;
}
// Good: Use buffered reading
public String readFileGood(String filename) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(Paths.get(filename))) {
StringBuilder content = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
return content.toString();
}
}
// Better: Use Files.readAllLines for small files
public List<String> readFileLines(String filename) throws IOException {
return Files.readAllLines(Paths.get(filename));
}
// Best: Use Files.readString (Java 11+)
public String readFileString(String filename) throws IOException {
return Files.readString(Paths.get(filename));
}
Network I/O¶
// Bad: Synchronous I/O blocking
public String fetchUrlBad(String url) throws IOException {
URLConnection connection = new URL(url).openConnection();
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream())
);
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
return response.toString();
}
// Good: Use HttpClient (Java 11+)
public CompletableFuture<String> fetchUrlAsync(String url) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body);
}
📈 JVM Tuning¶
Garbage Collection¶
# G1GC (Good for most applications)
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 MyApp
# ZGC (Low latency, large heaps)
java -XX:+UseZGC MyApp
# Parallel GC (High throughput)
java -XX:+UseParallelGC -XX:ParallelGCThreads=4 MyApp
Memory Settings¶
# Set heap size
java -Xms512m -Xmx2g MyApp
# Set metaspace size
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m MyApp
# Optimize for containers
java -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 MyApp
🔍 Performance Monitoring¶
JVM Tools¶
# JVisualVM - GUI monitoring
jvisualvm
# JConsole - Basic monitoring
jconsole
# JFR - Flight Recorder (Java 11+)
java -XX:StartFlightRecording=duration=60s,filename=recording.jfr MyApp
# JHSDB - Serviceability agent
jhsdb jmap --pid <pid> --heap
Profiling Tools¶
// Use JMH for microbenchmarks
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class MyBenchmark {
@Benchmark
public String stringConcatenation() {
String result = "";
for (int i = 0; i < 1000; i++) {
result += i;
}
return result;
}
@Benchmark
public String stringBuilderConcatenation() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i);
}
return sb.toString();
}
}
📚 Related Resources¶
- Java Best Practices - Prevent performance issues with good practices
- Java Common Mistakes - Avoid performance pitfalls
- Java Testing Frameworks - Performance testing with JUnit
- Java Resources - Performance tools and documentation
🔗 Related Performance Guides¶
- Time Complexity - Algorithm analysis
- Space Complexity - Memory optimization
- Algorithm Analysis - Performance measurement
- Optimization Techniques - General optimization strategies
🔗 Language-Specific Performance¶
- Python Performance Tips - Python optimization
- C Performance Tips - C optimization
- Oracle Performance Tips - Oracle optimization