The JVM divides memory into two main regions:

public void example() { int x = 10; // stored on stack String s = new String("hello"); // reference 's' on stack, object on heap } // After method returns: x and s are popped off stack. // The String object on heap is eligible for GC when no references remain.

The JVM heap is divided into generations to optimize GC performance, based on the observation that most objects die young (the generational hypothesis).

// JVM flags to view GC activity // -verbose:gc // -XX:+PrintGCDetails // -Xms256m -Xmx1g (initial and max heap size) // -XX:NewRatio=2 (Old:Young ratio, default 2 means 1/3 Young, 2/3 Old) // -XX:SurvivorRatio=8 (Eden:Survivor ratio within Young Gen)

Java provides several GC implementations, selectable via JVM flags:

// Explicitly requesting GC (not guaranteed to run immediately) System.gc(); // Checking heap usage at runtime Runtime rt = Runtime.getRuntime(); long usedMB = (rt.totalMemory() - rt.freeMemory()) / (1024 * 1024); long maxMB = rt.maxMemory() / (1024 * 1024); System.out.printf("Used: %dMB / Max: %dMB%n", usedMB, maxMB);

Java provides four reference strengths that affect when objects are garbage collected:

import java.lang.ref.*; // Soft reference — kept alive while memory is available SoftReference<byte[]> softRef = new SoftReference<>(new byte[1024 * 1024]); byte[] data = softRef.get(); // null if GC collected it // Weak reference — collected eagerly WeakReference<MyObject> weakRef = new WeakReference<>(new MyObject()); MyObject obj = weakRef.get(); // may return null after GC // WeakHashMap — entries removed when key is no longer strongly reachable Map<Object, String> cache = new WeakHashMap<>();

Java can still have memory leaks — situations where objects are reachable but no longer needed:

// BAD: potential memory leak — stream not closed on exception BufferedReader reader = new BufferedReader(new FileReader("file.txt")); String line = reader.readLine(); reader.close(); // not reached if readLine() throws // GOOD: try-with-resources — always closes the stream try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) { String line = reader.readLine(); } // BAD: ThreadLocal not removed — leaks in thread pools private static ThreadLocal<Connection> conn = new ThreadLocal<>(); // GOOD: always remove when done try { conn.set(getConnection()); // use connection... } finally { conn.remove(); }