← Back to Advanced Java Course | Chapter 11: Advanced & Security | Lesson 5 of 19

Java Performance Optimization

Tuning JVM Flags

Adjusting JVM heap flags like -Xms (initial heap size) and -Xmx (maximum heap size) lets you tune how much memory your application starts with and can grow into, which matters because too small a heap causes frequent GC pauses and too large a heap wastes system memory.

Example: Tuning JVM Flags

java
public class Main {
	public static void main(String[] args) {
		// Run with: java -Xms256m -Xmx512m Main
		Runtime runtime = Runtime.getRuntime();
		System.out.println("Max heap: " + runtime.maxMemory() / (1024 * 1024) + "MB");
	}
}

Choosing Efficient Collections

Choosing the right collection type for the job limits unnecessary lookup overhead -- use an ArrayList when you mostly iterate or index sequentially, and a HashMap when you need near O(1) lookups by key, rather than defaulting to whichever collection is most familiar.

Example: Choosing Efficient Collections

java
import java.util.*;
public class Main {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>(); // fast sequential iteration/indexing
		Map<String, Integer> map = new HashMap<>(); // near O(1) lookup by key
		list.add(1);
		map.put("key", 1);
		System.out.println(list.get(0) + " " + map.get("key"));
	}
}

Avoiding Object Allocation

Limiting unnecessary temporary object allocation reduces the workload the garbage collector has to handle, since every short-lived object still has to be scanned and reclaimed eventually -- a pattern especially worth watching inside hot loops that run millions of times.

Example: Avoiding Object Allocation

java
public class Main {
	public static void main(String[] args) {
		StringBuilder sb = new StringBuilder(); // reuses one buffer instead of many temporary Strings
		for (int i = 0; i < 1000; i++) {
			sb.append(i);
		}
		System.out.println(sb.length());
	}
}

Thread Pool Configuration

Using a thread pool via an Executor lets you reuse a fixed set of worker threads across many tasks instead of creating and destroying a new thread for every unit of work, which avoids the real startup and teardown cost that comes with each new OS thread.

Example: Thread Pool Configuration

java
import java.util.concurrent.*;
public class Main {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newFixedThreadPool(4); // reused workers, not a new thread per task
		for (int i = 0; i < 8; i++) pool.submit(() -> {});
		pool.shutdown();
		System.out.println("8 tasks handled by 4 reused threads");
	}
}

Lazy Initialization Pattern

Lazy initialization defers an expensive operation -- a database load, a large object construction -- until the moment it's actually needed, rather than paying that cost unconditionally at startup even for code paths that end up never being used.

Example: Lazy Initialization Pattern

java
public class Main {
	static class ExpensiveResource {
		private static ExpensiveResource instance;
		static ExpensiveResource get() {
			if (instance == null) instance = new ExpensiveResource(); // deferred until first actually needed
			return instance;
		}
	}
	public static void main(String[] args) {
		System.out.println(ExpensiveResource.get() != null);
	}
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.