Skip to main content

How Java Manages Memory Behind the Scenes

Understanding how Java allocates and deallocates memory is essential for writing efficient, fast, and reliable applications. While the JVM automates most of the heavy lifting, knowing what happens under the hood helps you avoid memory leaks, improve performance, and truly master Java.

🔍 1. Where Java Data Lives

Java stores data in different memory regions depending on whether it is a primitive value or a reference to an object.

🟦 a) Primitive Data Types

Examples: int, double, boolean, char, byte, short, long, float

Where they’re stored:

  • Java Stack (local primitives) — When primitive variables are declared inside a method, they live on the stack.
  • Heap (inside objects) — If primitives are part of an object, they live inside that object on the heap.
public void myMethod() {
    int count = 10;        // Stored on stack
    boolean isActive = true;
}

class MyClass {
    int value;          // Stored on the heap as part of the object
    static long total;  // Stored in Method Area (Metaspace)
}

🟩 b) Reference Data Types (Objects)

Objects do not store the value directly. A reference variable points to the actual object stored on the heap.

Where they’re stored: Heap (object), Stack (local reference), Heap (instance field reference), Metaspace (static reference).

public void anotherMethod() {
    String name = "Java";           // reference on stack
    // "Java" object stored on heap (String pool)
    MyObject obj = new MyObject();  // reference on stack
}

🏗️ 2. How Object Memory Is Allocated

Follow the lifecycle of an object.

  1. Declaration: MyObject obj; — only the reference; no object yet.
  2. Instantiation: obj = new MyObject(); — triggers class loading, heap allocation, default initialization, constructor invocation, and reference assignment.

Behind the scenes:

  • Class Loading: Metadata is placed in Metaspace.
  • Heap Allocation: Fields and references get space on the heap.
  • Default Initialization: primitives → 0, booleans → false, references → null.
  • Constructor Execution: custom initialization runs.
  • Reference Assignment: the variable points to the heap object.

♻️ 3. Garbage Collection — Java’s Automatic Memory Cleanup

Java automatically reclaims memory using Garbage Collection (GC). An object becomes eligible for GC when no active references point to it.

Example:
MyObject obj1 = new MyObject("A");
MyObject obj2 = new MyObject("B");

obj1 = obj2;  // Object "A" becomes unreachable → GC candidate
obj2 = null;  // Depending on other refs, object "B" may become unreachable

How GC Works (High level)

  1. Marking: Start from GC roots (stack locals, static variables, JNI refs, loaded classes) and mark reachable objects.
  2. Sweeping: Unmarked objects are removed and memory reclaimed.
  3. Compacting (optional): Live objects are moved to reduce fragmentation.

⚡ Generational Garbage Collection

Modern JVMs use a generational model because most objects are short-lived.

  • Young Generation: Eden (new objects) + Survivor spaces (S0/S1). Minor GCs are fast and frequent.
  • Old Generation (Tenured): Long-lived objects are promoted here. Major/Full GCs clean the entire heap and are more expensive.
  • Metaspace: Class metadata (replaced PermGen since Java 8).

Beneficial principle: Most objects die young — JVM optimizes around this to reduce pause times and improve throughput.

🔚 4. What Happens When References Are Removed?

Unreachable objects are marked, swept, and possibly finalized (although finalize() is deprecated — prefer Cleaner or try-with-resources for non-memory resources).

obj1 = obj2;   // Object A becomes unreachable
obj2 = null;   // Object B may become unreachable (if no other refs)

✅ Conclusion

Java’s memory model — with stack, heap, metaspace, and a sophisticated garbage collector — provides an automated, powerful system for managing memory. By understanding where primitives and objects live, how references work, and how GC identifies unused objects (especially with generational GC), you can write faster, more memory-efficient, and more robust Java applications.


Published on

Comments

Popular posts from this blog

Spring Boot on AWS EC2: Upload to S3 Securely Using IAM Role

Java Streams Intermediate Operations Explained with Examples

ConcurrentHashMap vs Synchronized HashMap in Java