What is Garbage Collection?
Imagine you're at a restaurant eating. After you finish, you don't wash your own dishes - the staff cleans up for you! Garbage Collection (GC) is like that restaurant staff for your program's memory - it automatically cleans up objects you're no longer using.
Real-World Analogy: Office Cleaning Service
- You (programmer): Create objects, use them, then forget about them
- Garbage Collector: Comes by after hours, identifies trash (unused objects), throws it away
- Result: Clean office (memory) ready for tomorrow!
Manual Memory Management (C/C++) vs Automatic (Java)
// C/C++ - Manual (you must remember to free memory!)
int* ptr = (int*)malloc(sizeof(int));
*ptr = 42;
// ... use ptr ...
free(ptr); // Must manually free! If you forget = memory leak!
// Java - Automatic (GC handles it!)
Person p = new Person("Alice");
// ... use p ...
p = null; // Object becomes eligible for GC
// GC automatically cleans it up - you don't worry about it!
Why Garbage Collection Matters
- Prevents Memory Leaks: Automatically frees unused memory
- Prevents Dangling Pointers: Can't access freed memory accidentally
- Developer Productivity: Focus on logic, not memory management
- Safety: Reduces crashes from memory errors
How Does GC Know What to Clean?
Reachability - The Key Concept
An object is reachable if you can access it through references. If unreachable, it's garbage!
public static void main(String[] args) {
Person p1 = new Person("Alice"); // Reachable via p1
Person p2 = new Person("Bob"); // Reachable via p2
p1 = null; // Alice object is now UNREACHABLE → Garbage!
p2 = new Person("Charlie"); // Bob becomes UNREACHABLE → Garbage!
}
Visualization:
┌─────────┐ ┌──────────────┐
│ p1 │────→│ Person:Alice │ Reachable ✓
└─────────┘ └──────────────┘
After p1 = null:
┌─────────┐ ┌──────────────┐
│ p1 │ null │ Person:Alice │ Unreachable ✗ → GARBAGE
└─────────┘ └──────────────┘
GC Roots - Starting Points
GC starts from "roots" and traces all reachable objects. Roots include:
- Local variables in stack
- Static variables
- Active threads
- JNI references
GC Root Tracing:
Start → Stack (local vars) → Objects they point to → Objects those point to...
Everything not traced = Garbage!
Generational Garbage Collection
Java's GC uses a clever insight: Most objects die young!
The Weak Generational Hypothesis
Observation:
• 90% of objects die within seconds/minutes of creation
• 10% survive long-term
Solution:
Split heap into generations, clean young objects frequently!
Heap Generations
┌────────────────────────────────────────────────┐
│ JAVA HEAP │
├────────────────────────────────────────────────┤
│ YOUNG GENERATION (33%) │
│ ┌──────────┬──────────────┬──────────────┐ │
│ │ Eden │ Survivor S0 │ Survivor S1 │ │
│ │ (90%) │ (5%) │ (5%) │ │
│ │ │ │ │ │
│ │ New objs │ Survived 1+ │ Survived 2+ │ │
│ │ born here│ GC cycles │ GC cycles │ │
│ └──────────┴──────────────┴──────────────┘ │
├────────────────────────────────────────────────┤
│ OLD GENERATION (Tenured) (67%) │
│ Long-lived objects that survived many GCs │
│ Cleaned less frequently │
└────────────────────────────────────────────────┘
Object Lifecycle
Step 1: Object is born
Person p = new Person();
→ Created in Eden space
Step 2: Minor GC happens (Eden fills up)
→ Dead objects: Cleaned up
→ Surviving objects: Moved to Survivor S0, age = 1
Step 3: Next Minor GC
→ Eden + S0 objects examined
→ Survivors moved to S1, age incremented (age = 2)
Step 4: Objects keep bouncing between S0 ↔ S1
→ Age increments each time
Step 5: Object reaches age threshold (default: 15)
→ Promoted to Old Generation (Tenured)
Step 6: Lives in Old Gen until Major GC
→ Eventually cleaned if unreachable
Types of Garbage Collection
1. Minor GC (Young Generation)
- Frequency: Very frequent (milliseconds/seconds)
- Speed: Fast (few milliseconds)
- Scope: Only Young Generation
- Trigger: Eden space fills up
- Impact: Minimal pause time
2. Major GC / Full GC (Whole Heap)
- Frequency: Infrequent (minutes/hours)
- Speed: Slow (hundreds of milliseconds - seconds)
- Scope: Entire heap (Young + Old)
- Trigger: Old Gen fills up, System.gc(), etc.
- Impact: Noticeable pause (Stop-The-World)
Stop-The-World Events
During GC, application threads must pause (Stop-The-World). Modern GCs minimize this!
Application Timeline:
App running... → [GC Pause 5ms] → App running... → [GC Pause 3ms] → ...
During GC pause:
✗ Application threads stopped
✓ GC threads working
Goal: Minimize pause time!
Garbage Collection Algorithms
1. Serial GC (Default for small apps)
// Enable with:
java -XX:+UseSerialGC MyApp
How it works:
• Single-threaded GC
• Stops all app threads
• Simple, low overhead
• Good for: Small apps, single-core machines
2. Parallel GC (Throughput Collector)
// Enable with:
java -XX:+UseParallelGC MyApp
How it works:
• Multi-threaded GC
• Uses multiple cores for GC
• Faster cleanup
• Good for: Batch processing, high throughput apps
3. G1 GC (Garbage First - Default Java 9+)
// Enable with (or default in Java 9+):
java -XX:+UseG1GC MyApp
How it works:
• Divides heap into regions
• Collects regions with most garbage first
• Predictable pause times
• Good for: Large heaps, low-latency apps
4. ZGC (Scalable Low-Latency GC - Java 11+)
// Enable with:
java -XX:+UseZGC MyApp
How it works:
• Ultra-low pause times (<10ms)
• Concurrent GC (runs alongside app)
• Handles huge heaps (terabytes)
• Good for: Ultra-low-latency requirements
Memory Leaks in Java - Yes, They Can Happen!
Even with GC, you can leak memory if you hold references to objects you no longer need.
Common Memory Leak Scenarios
1. Static Collections
public class Cache {
private static List<Object> cache = new ArrayList<>();
public static void add(Object obj) {
cache.add(obj); // Objects NEVER removed!
}
}
// Every object added stays in memory forever!
// Static collection is always reachable → Objects never GC'd
2. Unclosed Resources
FileInputStream fis = new FileInputStream("file.txt");
// ... use fis ...
// Never closed! File handle leaked!
try (FileInputStream fis = new FileInputStream("file.txt")) {
// use fis...
} // Automatically closed!
3. Event Listeners Not Removed
// BAD: Add listener but never remove
button.addActionListener(myListener);
// If button outlives listener, listener stays in memory!
// GOOD: Remove when done
button.removeActionListener(myListener);
Monitoring and Tuning GC
GC Logging
// Enable GC logging
java -Xlog:gc* -Xlog:gc:gc.log MyApp
// Output example:
[0.123s][info][gc] GC(0) Pause Young (Normal) 12M->2M(50M) 4.567ms
// │ │ │ │
// Type Before After Time
Heap Size Tuning
// Set initial heap size (Xms) and max heap size (Xmx)
java -Xms512m -Xmx2g MyApp
// -Xms512m: Start with 512MB heap
// -Xmx2g: Allow heap to grow up to 2GB
Best Practice: Set Xms = Xmx (avoid resizing overhead)
java -Xms2g -Xmx2g MyApp
Monitoring Tools
- jstat: Command-line GC statistics
- jconsole: Visual monitoring tool
- VisualVM: Advanced profiling
- Java Mission Control: Production monitoring
// View GC stats in real-time
jstat -gc 12345 1000
// │ │
// PID Every 1000ms
Best Practices
- ✅ Avoid creating unnecessary objects: Reuse when possible
- ✅ Use object pools for expensive objects: Database connections, threads
- ✅ Clear large collections when done: list.clear()
- ✅ Use weak references for caches: WeakHashMap, SoftReference
- ✅ Close resources: Try-with-resources or finally blocks
- ✅ Avoid finalizers: They delay GC and are unpredictable
- ✅ Profile before tuning: Measure, don't guess
- ❌ Don't call System.gc(): Let JVM decide
- ❌ Don't over-tune: Default settings work for most apps
Writing GC-Friendly Code
// BAD: Creates many temporary objects
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // Creates 1000 String objects!
}
// GOOD: Reuses single StringBuilder
StringBuilder result = new StringBuilder();
for (int i = 0; i < 1000; i++) {
result.append(i); // Single object!
}
Why It Matters
- Performance: Efficient GC = faster applications
- Scalability: Handle more users with same hardware
- Stability: Prevent OutOfMemoryErrors
- Cost: Better GC tuning = lower infrastructure costs
- User Experience: Low pause times = smooth applications
Garbage Collection is like a self-cleaning oven - it handles the dirty work so you can focus on cooking (coding)! Trust it, but understand how it works for when you need to tune performance.