Definition
- 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
Accountobject withnew 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.
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;
}
}
# 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.
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:
- Born in Eden.
new Account("SAV2001")allocates in Eden. - Survives Minor GC. If still referenced when Eden fills up, it moves to Survivor S0 or S1.
- Ages in Survivor spaces. Each Minor GC increments the object's age. It alternates between S0 and S1.
- Promoted to Old Gen. When the age reaches the tenuring threshold (default 15), it moves to Old Generation.
- Collected in Full GC. If the object eventually becomes unreachable, a Full GC reclaims its memory.
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.
# 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
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());
}
}
}
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:
// 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:
// 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
newis 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?
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.
---