Definition
GC algorithm:
- Mark: Start from GC roots (stacks, statics). Trace all references. Mark reachable objects.
- Sweep: Free memory of unmarked (dead) objects.
- Compact (optional): Move surviving objects together to eliminate fragmentation.
- Generational: Young Gen (short-lived) collected often. Old Gen (long-lived) collected rarely.
The bank vault (heap) periodically audits which safety deposit boxes (objects) are still claimed (reachable). Unclaimed boxes are emptied (garbage collected). Recently opened boxes (young gen) are audited frequently because most are temporary.
Understanding the Concepts
Generational heap
Heap: ┌─── Young Generation ───────────┐ ┌─── Old Generation ──┐ │ Eden │ Survivor 0 │ Survivor 1│ │ Long-lived objects │ │ (new objects land here) │ │ (survived many GCs) │ └─────────────────────────────────┘ └─────────────────────┘ Minor GC: collects Young Gen (fast, ~10ms) Full GC: collects entire heap (slow, ~100ms-seconds)
Object lifecycle
new Account()→ allocated in Eden.- Minor GC: if still reachable, moved to Survivor space. Age incremented.
- After N Minor GCs (default 15), promoted to Old Gen.
- Full GC: Old Gen collected when full or explicitly requested.
Common GC algorithms
| GC | Type | Best for |
|---|---|---|
| G1 | Default (Java 9+) | General-purpose, balanced |
| ZGC | Low-latency | <1ms pauses, large heaps |
| Shenandoah | Low-latency | Similar to ZGC |
| Parallel | Throughput | Batch processing |
| Serial | Single-thread | Small heaps, containers |
What should a senior engineer know?
- GC logs:
-Xlog:gc*(Java 9+) or-verbose:gc(legacy). Essential for tuning. - GC pause time goal:
-XX:MaxGCPauseMillis=200(G1 target). - Memory leaks in Java: Objects that are reachable but no longer needed (e.g., growing static collections).
What are common mistakes?
A common mistake is assuming that calling System.gc() guarantees garbage collection. The JVM treats this as a suggestion, not a command.
Wrong approach:
package com.bank.service;
public class AccountCleanup {
public void cleanupOldAccounts() {
// WRONG: relying on System.gc()
oldAccounts = null;
System.gc(); // No guarantee this runs
System.out.println("Memory freed"); // May not be true
}
}
Correct approach:
package com.bank.service;
public class AccountCleanup {
public void cleanupOldAccounts() {
// CORRECT: let the GC do its job, just remove references
oldAccounts.clear();
oldAccounts = null;
// Do not call System.gc() in production
// Monitor with -verbose:gc or JFR instead
}
}
Another common mistake is holding references to objects that are no longer needed, which prevents garbage collection and causes memory leaks in long-running banking applications.
What are the edge cases?
Edge cases in memory management can cause unexpected behavior in banking applications.
package com.bank.edge;
public class MemoryEdgeCases {
public static void main(String[] args) {
// Edge case 1: large arrays get allocated directly in Old Gen
byte[] largeBuffer = new byte[10 * 1024 * 1024]; // 10 MB
// This skips Eden and goes straight to Old Gen
// Edge case 2: String.intern() can cause memory issues
// if you intern millions of unique transaction IDs
for (int i = 0; i < 1_000_000; i++) {
String txnId = ("TXN-" + i).intern(); // Bad: fills String Table
}
// Edge case 3: SoftReference behavior changes under memory pressure
// Objects may be collected sooner than expected
}
}
- Humongous objects in G1. Objects larger than half a G1 region are allocated as humongous objects, bypassing the normal Eden allocation path.
- Metaspace leaks. Dynamic class generation (e.g., in ORM frameworks) can leak class metadata if classloaders are not properly garbage collected.
- Direct ByteBuffer. Off-heap memory allocated with
ByteBuffer.allocateDirect()is not counted in-Xmxand can causeOutOfMemoryError: Direct buffer memory.
What are common interview follow-up questions?
Interviewers often follow up with these related questions:
- How does this concept apply to a high-throughput banking transaction system?
- What are the performance implications in a production environment?
- How would you debug issues related to this concept?
- What tools would you use to monitor this in production?
- How does this behave differently in Java 8 vs Java 17+?
When answering follow-up questions, always relate your answer back to practical experience. For example, at National Bank of India, Rajesh debugged a production issue related to this concept by using JFR recordings and heap dumps. Providing concrete examples from banking systems shows the interviewer that you understand both the theory and the practice.
Visual Overview
new Account() → Eden
│ Minor GC
▼
Reachable? → Survivor (age++)
Not reachable? → freed
│ age > threshold
▼
Old Generation
│ Old Gen full
▼
Full GC (slow, STW)
GC types: G1 (default), ZGC (low-latency), Parallel (throughput)
Key Takeaway
GC automatically reclaims unreachable objects. It traces from GC roots, marks live objects, and frees the rest. The generational hypothesis (most objects die young) enables fast, frequent young generation collection and rare, slower old generation collection.
One sentence to remember: GC marks reachable objects and frees the rest – young gen is collected often and cheaply, old gen rarely and expensively.
---