1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: Java has 4 reference types: (1) Strong (normal, Object obj = new Object()—never GC'd while reachable), (2) Soft (SoftReference<T>—GC'd before OOM, for caches), (3) Weak (WeakReference<T>—GC'd at next cycle, WeakHashMap), (4) Phantom (PhantomReference<T>—cleanup actions, post-finalization). Weak = "I want it, but GC can take it". Soft = "Keep it if memory available". Phantom = "Tell me after GC'd".
- Use: Soft for caches, Weak for metadata, Phantom for cleanup!
Think of library books. Strong = you own the book (never taken). Soft = library keeps popular books (discards if space needed). Weak = you borrowed, library can reclaim anytime. Phantom = notification when book destroyed (for inventory update)!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- Strong: Owned property (yours)
- Soft: Lease with option (kept if space)
- Weak: Temporary pass (revoked anytime)
- Phantom: Demolition notice (cleanup hook)
3. Technical Mastery (The "Deep Dive")
Reference Types Comparison
| Type | GC Behavior | Use Case | Example |
|---|---|---|---|
| Strong | Never GC'd (while reachable) | Normal objects | Object obj = new Object() |
| Soft | GC'd before OOM | Memory-sensitive caches | SoftReference<Image> cache |
| Weak | GC'd at next cycle | Canonicalizing mappings | WeakHashMap<Key, V> |
| Phantom | After finalization | Post-mortem cleanup | PhantomReference<Resource> |
4. Interactive & Applied Code
java
import java.lang.ref.*;
import java.util.*;
public class ReferenceTypesDemo {
public static void main(String[] args) throws Exception {
demonstrateStrong();
demonstrateSoft();
demonstrateWeak();
demonstratePhantom();
demonstrateWeakHashMap();
}
// 1. Strong reference (default)
static void demonstrateStrong() {
System.out.println("=== STRONG REFERENCE ===");
Object obj = new Object(); // Strong reference
System.gc();
// obj NEVER GC'd while reachable
System.out.println("Strong ref: " + obj);
obj = null; // Remove reference
System.gc();
// NOW eligible for GC
}
// 2. Soft reference (cache-friendly)
static void demonstrateSoft() throws Exception {
System.out.println("\n=== SOFT REFERENCE ===");
Object obj = new Object();
SoftReference<Object> softRef = new SoftReference<>(obj);
obj = null; // Remove strong reference
System.out.println("Before GC: " + softRef.get()); // Still available
System.gc();
Thread.sleep(100);
// May still be available (GC keeps if memory available)
System.out.println("After GC: " + softRef.get());
// Soft refs cleared only before OutOfMemoryError
// Good for caches! list<SoftReference<Image>> imageCache = ...
}
// 3. Weak reference (weak connection)
static void demonstrateWeak() throws Exception {
System.out.println("\n=== WEAK REFERENCE ===");
Object obj = new Object();
WeakReference<Object> weakRef = new WeakReference<>(obj);
System.out.println("Before null: " + weakRef.get()); // Available
obj = null; // Remove strong reference
System.gc();
Thread.sleep(100);
// Weak ref cleared at next GC
System.out.println("After GC: " + weakRef.get()); // null
}
// 4. Phantom reference (cleanup hook)
static void demonstratePhantom() throws Exception {
System.out.println("\n=== PHANTOM REFERENCE ===");
ReferenceQueue<Object> queue = new ReferenceQueue<>();
Object obj = new Object();
PhantomReference<Object> phantomRef = new PhantomReference<>(obj, queue);
System.out.println("get() always null: " + phantomRef.get()); // Always null!
obj = null; // Remove strong reference
System.gc();
Thread.sleep(100);
// Check queue for notification
Reference<?> ref = queue.poll();
if (ref != null) {
System.out.println("Object collected, cleanup now!");
// Perform cleanup
ref.clear();
}
}
// WeakHashMap demonstration
static void demonstrateWeakHashMap() throws Exception {
System.out.println("\n=== WEAKHASHMAP ===");
Map<Object, String> weakMap = new WeakHashMap<>();
Object key1 = new Object();
Object key2 = new Object();
weakMap.put(key1, "Value 1");
weakMap.put(key2, "Value 2");
System.out.println("Before: " + weakMap.size()); // 2
key1 = null; // Remove strong reference to key1
System.gc();
Thread.sleep(100);
System.out.println("After GC: " + weakMap.size()); // 1 (key1 entry removed!)
}
}
// Real-world example: Image cache
class ImageCache {
// Soft references: Keep images if memory available
private Map<String, SoftReference<Image>> cache = new HashMap<>();
Image getImage(String path) {
SoftReference<Image> ref = cache.get(path);
if (ref != null) {
Image img = ref.get();
if (img != null) {
return img; // Cache hit
}
}
// Cache miss: Load image
Image img = loadImage(path);
cache.put(path, new SoftReference<>(img));
return img;
}
Image loadImage(String path) {
return new Image(path);
}
}
class Image {
String path;
Image(String path) { this.path = path; }
}
// Canonical mapping with WeakHashMap
class StringPool {
private static WeakHashMap<String, WeakReference<String>> pool = new WeakHashMap<>();
static String intern(String s) {
WeakReference<String> ref = pool.get(s);
if (ref != null) {
String cached = ref.get();
if (cached != null) {
return cached; // Reuse
}
}
pool.put(s, new WeakReference<>(s));
return s;
}
}5. The Comparison & Decision Layer
| Ref Type | When to Use |
|---|---|
| Strong | Normal objects (default) |
| Soft | Memory-sensitive caches (images, large data) |
| Weak | Canonicalizing mappings, metadata |
| Phantom | Pre-mortem cleanup (resource release) |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "Soft vs Weak reference?" Answer:
- Soft: GC'd only before OutOfMemoryError (kept if memory available)
- Weak: GC'd at next GC cycle (regardless of memory)
java
// Soft: For caches
SoftReference<Image> soft = new SoftReference<>(new Image());
System.gc();
soft.get(); // ✅ Likely still available (if memory OK)
// Weak: For weak mappings
WeakReference<String> weak = new WeakReference<>(new String("Hi"));
System.gc();
weak.get(); // ❌ Likely null (GC'd)Pro-Tip: WeakHashMap use case:
java
// Metadata storage (doesn't prevent GC of keys)
WeakHashMap<Object, Metadata> metadata = new WeakHashMap<>();
Object obj = new Object();
metadata.put(obj, new Metadata());
// obj used elsewhere...
obj = null; // No more strong refs
System.gc();
// Entry automatically removed from WeakHashMap!
// Prevents memory leak!