1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: Memory profiling = analyzing heap usage to find leaks/inefficiencies.
- Tools: JVisualVM (free, bundled), Eclipse MAT (heap dump analysis), JProfiler/YourKit (commercial). Heap dump = snapshot of heap memory.
- Create: -XX:+HeapDumpOnOutOfMemoryError or jmap -dump.
- MAT features: Dominator tree (what holds memory), leak suspects (suspects report), histogram (object counts), shallow vs retained size.
- Golden metric: Retained size = total memory freed if object GC'd!
Think of home inspection. Heap dump = blueprint of house. MAT = inspector finding structural issues. Dominator tree = load-bearing walls (remove wall → whole section collapses). Leak suspects = "This room accumulates junk!" Histogram = inventory (200 chairs? Why?)!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- Heap dump: Photo of messy room
- Dominator tree: Main culprits holding space
- Shallow size: Object itself
- Retained size: Object + everything it holds
3. Technical Mastery (The "Deep Dive")
Memory Profiling Tools
| Tool | Type | Best For |
|---|---|---|
| JVisualVM | Live monitor | Real-time monitoring |
| Eclipse MAT | Heap analyzer | Deep leak analysis |
| JConsole | Live monitor | JMX monitoring |
| YourKit | Commercial | Advanced profiling |
| JProfiler | Commercial | Advanced profiling |
4. Interactive & Applied Code
bash
# ========================================
# CREATING HEAP DUMPS
# ========================================
# Automatic on OOM
java -XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/tmp/heap.hprof \
-jar app.jar
# Manual with jmap
jmap -dump:live,format=b,file=heap.hprof <pid>
# Find PID
jps
# ========================================
# ANALYZING WITH ECLIPSE MAT
# ========================================
# 1. Open heap.hprof in MAT
# 2. Run "Leak Suspects" report
# 3. Check "Dominator Tree"
# 4. Look at "Histogram"Java code for memory monitoring:
java
import java.lang.management.*;
import java.util.*;
public class MemoryProfilingDemo {
public static void main(String[] args) {
demonstrateMemoryMonitoring();
demonstrateMemoryPools();
demonstrateGCStats();
}
// Basic memory monitoring
static void demonstrateMemoryMonitoring() {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
System.out.println("=== MEMORY USAGE ===");
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
System.out.println("Heap:");
printMemoryUsage(heapUsage);
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
System.out.println("\nNon-Heap:");
printMemoryUsage(nonHeapUsage);
}
static void printMemoryUsage(MemoryUsage usage) {
System.out.println(" Init: " + usage.getInit() / 1024 / 1024 + " MB");
System.out.println(" Used: " + usage.getUsed() / 1024 / 1024 + " MB");
System.out.println(" Committed: " + usage.getCommitted() / 1024 / 1024 + " MB");
System.out.println(" Max: " + usage.getMax() / 1024 / 1024 + " MB");
}
// Memory pools (Eden, Survivor, Old, etc.)
static void demonstrateMemoryPools() {
System.out.println("\n=== MEMORY POOLS ===");
List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean pool : pools) {
System.out.println("\n" + pool.getName() + ":");
System.out.println(" Type: " + pool.getType());
MemoryUsage usage = pool.getUsage();
System.out.println(" Used: " + usage.getUsed() / 1024 / 1024 + " MB");
System.out.println(" Max: " + usage.getMax() / 1024 / 1024 + " MB");
}
}
// GC statistics
static void demonstrateGCStats() {
System.out.println("\n=== GC STATISTICS ===");
List<GarbageCollectorMXBean> gcBeans =
ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
System.out.println("\n" + gcBean.getName() + ":");
System.out.println(" Collections: " + gcBean.getCollectionCount());
System.out.println(" Time: " + gcBean.getCollectionTime() + " ms");
System.out.println(" Avg: " +
(gcBean.getCollectionTime() / Math.max(1, gcBean.getCollectionCount())) + " ms");
}
}
}
// Trigger OOM for heap dump
class OOMDemo {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
try {
while (true) {
list.add(new byte[1024 * 1024]); // 1MB
}
} catch (OutOfMemoryError e) {
System.out.println("OOM! Heap dump created");
// Analyze heap.hprof with MAT
}
}
}5. The Comparison & Decision Layer
| Metric | Meaning |
|---|---|
| Shallow size | Object size only |
| Retained size | Object + all it retains |
| Dominator | Object removal frees dominated objects |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "Shallow vs Retained size?" Answer:
- Shallow size: Size of object itself (header + fields)
- Retained size: Shallow + all objects only reachable through it
text
Object A (10 bytes) → Object B (20 bytes) → Object C (30 bytes)
↘ Object D (40 bytes)
Shallow size of A: 10 bytes (just A)
Retained size of A: 10+20+30+40 = 100 bytes (A + B,C,D only reachable via A)
If A is GC'd → 100 bytes freed!Pro-Tip: MAT Leak Suspects:
text
Leak Suspects Report finds:
1. Objects with large retained size
2. Collections that keep growing
3. ClassLoaders not unloaded
4. Thread locals holding data
Common suspects:
- Static collections (never cleared)
- Caches (unbounded)
- Listeners (not removed)