Understanding Java Reference Types and Garbage Collection in JDK 8
This article explains the four kinds of Java references—strong, soft, weak, and phantom—how they interact with the garbage collector, the role of ReferenceQueue, and provides practical code examples and JVM internal details to help developers manage object lifecycles effectively.
In Java, object reachability is determined by reference types, which are the basis of garbage‑collection algorithms such as reference counting and reachability analysis.
Reference
Java distinguishes between primitive types (byte, short, int, long, float, double, char, boolean) and reference types (classes, interfaces, arrays, enums, annotations). A reference works like a pointer in C, allowing programs to manipulate heap objects.
Four Reference Strengths (JDK 1.2+)
Strong Reference
Soft Reference
Weak Reference
Phantom (or Ghost) Reference
The JVM treats each kind differently, enabling developers to influence object lifetimes.
Strong Reference
A typical assignment such as Object obj = new Object(); creates a strong reference. Objects reachable through strong references are never reclaimed, even if memory is low, which can lead to memory leaks unless the reference is cleared or goes out of scope.
public class StrongReferenceDemo {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = o1;
o1 = null;
System.gc();
System.out.println(o1); // null
System.out.println(o2); // java.lang.Object@xxxx
}
}In the demo, o2 holds a strong reference to the object, so it survives GC.
Soft Reference
Implemented via java.lang.ref.SoftReference , soft references are cleared only when the JVM is about to run out of memory. They are ideal for caches that should stay in memory while space permits.
// VM options: -Xms5m -Xmx5m
public class SoftReferenceDemo {
public static void main(String[] args) {
softRefMemoryEnough();
System.out.println("--- Memory insufficient case ---");
softRefMemoryNotEnough();
}
private static void softRefMemoryEnough() {
Object o1 = new Object();
SoftReference
s1 = new SoftReference<>(o1);
System.out.println(o1);
System.out.println(s1.get());
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(s1.get());
}
private static void softRefMemoryNotEnough() {
Object o1 = new Object();
SoftReference
s1 = new SoftReference<>(o1);
System.out.println(o1);
System.out.println(s1.get());
o1 = null;
byte[] bytes = new byte[10 * 1024 * 1024]; // allocate 10 MiB
System.out.println(o1);
System.out.println(s1.get());
}
}When memory is scarce, the soft‑referenced object is reclaimed, as shown by the OutOfMemoryError output.
Weak Reference
Created with java.lang.ref.WeakReference , weak references are cleared at the next GC cycle regardless of memory pressure. They are commonly used for canonicalizing maps such as WeakHashMap .
public class WeakReferenceDemo {
public static void main(String[] args) {
Object o1 = new Object();
WeakReference
w1 = new WeakReference<>(o1);
System.out.println(o1);
System.out.println(w1.get());
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(w1.get()); // likely null
}
}Phantom Reference
Phantom (or ghost) references, implemented via java.lang.ref.PhantomReference , never return the referent (their get() always yields null ). They must be paired with a ReferenceQueue to receive a notification when the object is finalized, allowing custom cleanup actions.
public class PhantomReferenceDemo {
public static void main(String[] args) throws InterruptedException {
Object o1 = new Object();
ReferenceQueue
queue = new ReferenceQueue<>();
PhantomReference
phantom = new PhantomReference<>(o1, queue);
System.out.println(o1);
System.out.println(queue.poll()); // null
System.out.println(phantom.get()); // null
o1 = null;
System.gc();
Thread.sleep(3000);
System.out.println(queue.poll()); // reference enqueued
System.out.println(phantom.get()); // still null
}
}ReferenceQueue
A ReferenceQueue receives references whose referents have been reclaimed. Both SoftReference , WeakReference , and PhantomReference can be constructed with a queue, enabling applications to react when an object disappears.
JDK 8 Reference Implementation (Key Points)
The abstract class java.lang.ref.Reference is the common superclass for all reference types. It stores the referent, an optional queue, and a linked‑list pointer used by the JVM’s internal ReferenceHandler thread.
public abstract class Reference
{
private T referent;
volatile ReferenceQueue
queue;
volatile Reference next;
private transient Reference
discovered;
private static Reference
pending = null;
Reference(T referent) { this(referent, null); }
Reference(T referent, ReferenceQueue
queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
public T get() { return referent; }
public void clear() { referent = null; }
public boolean isEnqueued() { return queue == ReferenceQueue.ENQUEUED; }
public boolean enqueue() { return queue.enqueue(this); }
}The JVM creates a high‑priority daemon thread named Reference Handler that moves pending references onto their queues, enabling the application‑level code to poll or remove them.
Practical Uses
Soft references are used in cache implementations (e.g., MyBatis SoftCache ), weak references power WeakHashMap and ThreadLocal internals, while phantom references allow post‑finalization cleanup.
For further reading, see the original sources: Juejin article, KD Gregory’s blog, CSDN tutorial, Throw‑able Club post, and the book "深入理解Java虚拟机".
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.