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

Java Memory Management

Stack vs Heap Memory

Java allocates local variables and method call frames on each thread's own Stack, which is fast and automatically cleaned up when a method returns, while actual object instances live in the shared Heap, which is managed by the garbage collector instead.

Example: Stack vs Heap Memory

java
public class Main {
	static class Point { int x; } // instances live on the Heap
	public static void main(String[] args) {
		int localVar = 5; // lives on the Stack, cleaned up when main() returns
		Point p = new Point(); // the reference is on the Stack, the object itself is on the Heap
		System.out.println(localVar + " " + p);
	}
}

Memory Leak Identification

Memory leaks in Java happen when code retains references to objects it no longer actually needs -- a growing static list, or an unclosed listener registration -- which prevents the garbage collector from reclaiming those objects even though the program will never use them again.

Example: Memory Leak Identification

java
import java.util.*;
public class Main {
	static List<Object> cache = new ArrayList<>(); // static list -- keeps growing, never cleared
	public static void main(String[] args) {
		for (int i = 0; i < 5; i++) cache.add(new Object());
		System.out.println("Cache size: " + cache.size() + " -- these can never be garbage collected");
	}
}

OutOfMemoryError (OOM)

The JVM throws an OutOfMemoryError when heap allocation requests exceed the memory actually available to it, which usually signals either a genuine memory leak or a heap size limit (-Xmx) configured too small for the application's real workload.

Example: OutOfMemoryError (OOM)

java
public class Main {
	public static void main(String[] args) {
		try {
			int[] huge = new int[Integer.MAX_VALUE]; // exceeds available heap
		} catch (OutOfMemoryError e) {
			System.out.println("Caught OutOfMemoryError: heap exceeded");
		}
	}
}

Metaspace Configuration

Class metadata -- the compiled structure of loaded classes themselves, not object instances -- lives in Metaspace, a dynamically-sized memory region that draws from native (off-heap) system memory rather than the regular Java heap.

Example: Metaspace Configuration

java
public class Main {
	public static void main(String[] args) {
		// Run with: java -XX:MaxMetaspaceSize=128m Main
		System.out.println("Class metadata lives in Metaspace, drawn from native memory, not the heap");
	}
}

Finalization and Cleaner API

The Cleaner API offers a safer, more predictable way to release off-heap resources (like native memory or file handles) tied to an object's lifecycle, replacing the old, unreliable Object.finalize() mechanism that the JVM never guaranteed would run promptly, if at all.

Example: Finalization and Cleaner API

java
import java.lang.ref.Cleaner;
public class Main {
	static final Cleaner cleaner = Cleaner.create();
	public static void main(String[] args) {
		Object resource = new Object();
		cleaner.register(resource, () -> System.out.println("Cleaned up off-heap resource"));
		resource = null;
		System.gc();
	}
}

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.