Java Garbage Collection and Memory Management

The Auto-Magic of the JVM

Java’s biggest selling point is its managed memory. While the Garbage Collector (GC) frees us from manual deallocation, it’s not a “get out of jail free” card. Understanding its mechanics is key to building low-latency systems.

Core Concepts

1. Generations (The Hip Hypothesis)

Most objects die young. Therefore, the JVM divides the heap into:

  • Young Generation (Eden + Survivor): Where new objects are born. Frequent, fast “Minor GCs” occur here.
  • Old Generation: Where long-lived objects end up. Expensive “Major GCs” occur here.

2. Modern Collectors

  • G1 (Garbage First): The default. Divides heap into regions and targets those with the most “garbage.”
  • ZGC (Z Garbage Collector): A scalable, low-latency collector (Java 11+). It performs most work concurrently with application threads, keeping pause times under 10ms regardless of heap size.

Practice Exercise: Preventing Resource Leaks

Even with a GC, you can “leak” resources like File Handles or DB Connections if you don’t close them.

The Old Way (Risky)

FileInputStream fis = null;
try {
    fis = new FileInputStream("test.txt");
    // do work
} catch (IOException e) { ... }
finally {
    if (fis != null) fis.close(); // Boilerplate and error-prone
}

The Modern Way (Try-with-resources)

Any class implementing AutoCloseable can be used here. Java ensures the resource is closed even if an exception occurs.

try (FileInputStream fis = new FileInputStream("test.txt")) {
    // do work
} catch (IOException e) {
    // fis is guaranteed to be closed here!
}

Why This Works

  • Reachability Analysis: The GC starts from “Roots” (Thread stacks, Static variables) and traces all references. Anything NOT reached is considered garbage.
  • Stop-The-World (STW): Most collectors must pause all application threads to safely move objects. ZGC minimizes this by using “colored pointers” and “load barriers” to handle object movement while the app is running.

Common Memory Leak: The Static Collection

A static HashMap that only grows and never removes items is the most common cause of OutOfMemoryError. The GC sees that the objects are still “reached” from the static Root, so it can never reclaim them.

Summary

GC is your ally, not your substitute. By choosing the right collector (like ZGC for latency-sensitive apps) and using proper constructs (like try-with-resources), you ensure your Java application remains stable and responsive.