Back to Memory Management & GC
Q2Question 2 of 21

How is heap memory divided?

Key Concepts
  • Young Gen: Eden + Survivor 0 + Survivor 1
  • Old Gen (Tenured): objects surviving many minor GCs
  • New objects → Eden; surviving → Survivor; old enough → Old Gen
  • Young Gen is small and collected often (Minor GC)
  • Old Gen is larger and collected rarely (Full GC)

Definition

Heap = Young Gen (Eden + 2 Survivors) + Old Gen. New objects go to Eden. Survivors hold objects that survived GC. Old Gen holds long-lived objects.

- Young Gen: Eden + Survivor 0 + Survivor 1\n- Old Gen (Tenured): objects surviving many minor GCs\n- New objects → Eden; surviving → Survivor; old enough → Old Gen\n- Young Gen is small and collected often (Minor GC)\n- Old Gen is larger and collected rarely (Full GC)

Understanding the Concepts

How is heap memory divided?

The JVM heap is divided into two main regions: the Young Generation and the Old Generation. Each region has a specific purpose in the lifecycle of objects.

The Young Generation is where all new objects are created. It is further divided into three spaces:

  • Eden is where objects are born. When Rajesh creates a new Account object with new Account("SAV2001"), the JVM allocates it in Eden.
  • Survivor 0 (S0) and Survivor 1 (S1) are two equally-sized spaces that hold objects that survived at least one garbage collection cycle.

The Old Generation (also called Tenured) holds objects that have survived many garbage collection cycles in the Young Generation. These are long-lived objects like connection pools, caches, and configuration objects.

JAVA
package com.bank.memory;

import java.math.BigDecimal;

public class HeapDivisionDemo {
    // This static cache lives in Old Gen after promotion
    private static final java.util.Map<String, BigDecimal> rateCache = new java.util.HashMap<>();

    public static void main(String[] args) {
        // New objects start in Eden (Young Gen)
        Account account = new Account("SAV2001", new BigDecimal("50000.00"));

        // Short-lived objects die in Eden during Minor GC
        for (int i = 0; i < 1000; i++) {
            String temp = "TXN-" + i;  // Born and dies in Eden
        }

        // Long-lived objects get promoted to Old Gen
        rateCache.put("SAVINGS", new BigDecimal("4.5"));
        rateCache.put("CURRENT", new BigDecimal("0.0"));
    }
}

class Account {
    private String id;
    private BigDecimal balance;
    Account(String id, BigDecimal balance) {
        this.id = id;
        this.balance = balance;
    }
}
TEXT
# JVM flags to control heap division
-Xms512m                     # Initial heap size
-Xmx2g                       # Maximum heap size
-XX:NewRatio=2                # Old:Young ratio = 2:1
-XX:SurvivorRatio=8           # Eden:Survivor ratio = 8:1
-XX:MaxTenuringThreshold=15   # GC cycles before promotion

Why is the heap divided this way?

The division exists because most objects die young. In a banking transaction service, each request creates temporary objects like request DTOs, validation results, and log messages. These objects live for milliseconds and die quickly. By isolating them in the small Young Generation, the GC can collect them frequently and cheaply with a Minor GC.

Long-lived objects like the database connection pool, the Spring application context, and cached interest rates survive many collections and get promoted to the Old Generation. The Old Generation is collected less frequently because its objects rarely die.

JAVA
package com.bank.service;

import java.math.BigDecimal;

public class TransactionProcessor {
    // Long-lived: stays in Old Gen
    private final AccountRepository repository = new AccountRepository();

    public void processTransfer(String from, String to, BigDecimal amount) {
        // Short-lived: born and dies in Young Gen
        TransferRequest request = new TransferRequest(from, to, amount);
        ValidationResult result = validate(request);
        if (result.isValid()) {
            repository.debit(from, amount);
            repository.credit(to, amount);
        }
        // request and result are garbage after this method returns
    }

    private ValidationResult validate(TransferRequest req) {
        return new ValidationResult(true);
    }
}

class TransferRequest {
    String from, to;
    BigDecimal amount;
    TransferRequest(String f, String t, BigDecimal a) { from=f; to=t; amount=a; }
}
class ValidationResult {
    boolean valid;
    ValidationResult(boolean v) { valid = v; }
    boolean isValid() { return valid; }
}
class AccountRepository {
    void debit(String id, BigDecimal amt) {}
    void credit(String id, BigDecimal amt) {}
}

How does an object move through the heap?

An object follows a specific path through the heap:

  1. Born in Eden. new Account("SAV2001") allocates in Eden.
  2. Survives Minor GC. If still referenced when Eden fills up, it moves to Survivor S0 or S1.
  3. Ages in Survivor spaces. Each Minor GC increments the object's age. It alternates between S0 and S1.
  4. Promoted to Old Gen. When the age reaches the tenuring threshold (default 15), it moves to Old Generation.
  5. Collected in Full GC. If the object eventually becomes unreachable, a Full GC reclaims its memory.
DIAGRAM
  Eden   ──Minor GC──▶  S0 ──Minor GC──▶  S1 ──(age >= threshold)──▶  Old Gen
  (birth)              (age=1)            (age=2)                      (tenured)

How do you monitor heap division?

Use JVM flags and tools to monitor heap usage in production banking systems.

BASH
# Print GC details including generation sizes
java -Xlog:gc*:file=gc.log:time,uptime,level,tags -jar bank-service.jar

# Use jstat to monitor heap in real time
jstat -gcutil <pid> 1000
JAVA
package com.bank.monitoring;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;

public class HeapMonitor {
    public static void main(String[] args) {
        for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
            System.out.printf("Pool: %-30s Used: %,d bytes%n",
                pool.getName(), pool.getUsage().getUsed());
        }
    }
}
TEXT
Pool: Eden Space                      Used: 12,345,678 bytes
Pool: Survivor Space                  Used: 1,234,567 bytes
Pool: Tenured Gen                     Used: 45,678,901 bytes
Pool: Metaspace                       Used: 34,567,890 bytes

What are common mistakes?

A common mistake is setting the Young Generation too small, which causes frequent Minor GCs and increases promotion to Old Gen.

Wrong approach:

JAVA
// WRONG: tiny Young Gen forces premature promotion
// java -Xmx2g -XX:NewRatio=10 -jar bank-service.jar
// Young Gen = ~180MB, Old Gen = ~1.8GB
// Short-lived transaction objects get promoted too quickly

Correct approach:

JAVA
// CORRECT: balanced ratio for transaction-heavy workload
// java -Xmx2g -XX:NewRatio=2 -jar bank-service.jar
// Young Gen = ~680MB, Old Gen = ~1.3GB
// Most transaction objects die in Young Gen as intended

What should a senior engineer know?

  • TLAB (Thread Local Allocation Buffer). Each thread gets its own small buffer in Eden for lock-free allocation. This is why new is extremely fast in Java.
  • Humongous objects in G1. Objects larger than half a G1 region skip Young Gen and go directly to a special humongous region.
  • Premature promotion. If the Survivor spaces are too small, objects get promoted to Old Gen before they die, causing more frequent Full GCs.
  • Adaptive sizing. The JVM dynamically adjusts Eden and Survivor sizes based on runtime behavior unless you disable it with -XX:-UseAdaptiveSizePolicy.

What are the edge cases?

JAVA
package com.bank.edge;

public class HeapEdgeCases {
    public static void main(String[] args) {
        // Edge case 1: Large array goes directly to Old Gen
        byte[] largeReport = new byte[5 * 1024 * 1024]; // 5 MB

        // Edge case 2: Many short-lived objects can still trigger Full GC
        // if they promote faster than Old Gen can handle
        java.util.List<byte[]> list = new java.util.ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add(new byte[1024 * 1024]);
            if (i % 10 == 0) list.clear(); // Simulates batch processing
        }
    }
}

What are common interview follow-up questions?

  • What is the default ratio between Young Gen and Old Gen?
  • How does the Survivor ratio affect GC behavior?
  • What happens when both Survivor spaces are full?
  • How does G1 handle heap division differently from Serial/Parallel GC?
  • What JVM flags control the sizes of each region?

Key Takeaway

Heap = Young Gen (Eden + 2 Survivors) + Old Gen. New objects go to Eden. Survivors hold objects that survived GC. Old Gen holds long-lived objects.\n\n

Remember one sentence: How is heap memory divided (Young Generation with Eden/Survivor, Old Generation, Metaspace) is fundamental knowledge for Java interviews and production banking work.

---

Share
Use ← → arrow keys to navigate