Understanding Strong and Weak References in Java
Strong References
Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. For example:
Object obj = new Object();
If the program faces memory shortages, rather than collecting objects with strong references, the JVM will throw an OutOfMemoryError, terminating the application unexpectedly. To allow garbage collection of strongly referenced objects, you need to explicitly set the reference to null when it is no longer needed:
obj = null;
When the reference is set to null, or the reference falls out of scope (e.g., in a method, when execution completes), the garbage collector considers the object unreferanced and eligible for recycling. For illustration:
public void demonstrateStrongReference() {
Object tempObject = new Object();
// Perform other operations
}
Here, the tempObject reference resides in the stack, while the actual object exists in the heap. Once the method finishes execution and its stack frame is removed, the object's reference count drops to zero, allowing garbage collection. However, if the object is referenced globally, you must explicitly set it to null to make it collectible becuase strong references do not allow automatic garbage collection.
A similar approach is implemented in the ArrayList class's clear() method to handle memory cleanup:
public void clear() {
modCount++;
// Enable garbage collection
for (int i = 0; i < size; i++) {
elementData[i] = null;
}
size = 0;
}
In this method, elements in the internal elementData array are individually assigned null, removing their strong references. This allows memory reclamation for objects referenced in the array without requiring reallocation of the array memory during subsequent operations like add. This approach is particularly advantageous for collections storing reference types.
Weak References
Weak references are a special type of reference where objects are eligible for garbage collection as soon as they are weakly referenced. When the garbage collector encounters objects with only weak references, it reclaims their memory without verifying weather there is sufficient memory space available. However, because garbage collection threads generally run with lower priority, this reclamation may not occur immediately.
Example:
String temp = new String("abc");
WeakReference<String> weakRef = new WeakReference<>(temp);
temp = null;
To make a weakly referenced object eligible for garbage collection sooner, you can manually invoke garbage collection:
temp = null;
System.gc();
Weak references are suitable for holding objects that are infrequently accessed but still need to be retrievable when required. This ensures that these objects won’t hinder the garbage collector's operations. You can also restore a weakly referenced object to a strong reference when needed:
String temp = new String("abc");
WeakReference<String> weakRef = new WeakReference<>(temp);
// Convert back to strong reference
String strongRef = weakRef.get();
Weak references are often paired with reference queues for advanced functionality. When the object being weakly referenced is garbage collected, its weak reference is automatically added to the associated reference queue for tracking.
Here’s an example demonstrating this:
public class DemoGCTarget {
public String identifier;
byte[] buffer = new byte[1024]; // Memory placeholder
public DemoGCTarget(String identifier) {
this.identifier = identifier;
}
@Override
protected void finalize() {
System.out.println("Finalizing object: " + identifier);
}
}
public class WeakReferenceExample {
private static final ReferenceQueue<DemoGCTarget> referenceQueue = new ReferenceQueue<>();
public static void main(String[] args) throws InterruptedException {
LinkedList<DemoGCTargetWeakReference> weakReferences = new LinkedList<>();
// Creating weakly referenced objects and storing them in the list
for (int i = 0; i < 5; i++) {
DemoGCTarget target = new DemoGCTarget(String.valueOf(i));
DemoGCTargetWeakReference weakRef = new DemoGCTargetWeakReference(target, referenceQueue);
weakReferences.add(weakRef);
System.out.println("Created weak reference for: " + target.identifier);
}
System.gc(); // Trigger garbage collection
Thread.sleep(6000); // Allow GC threads to run
// Process items in the reference queue
Reference<? extends DemoGCTarget> ref;
while ((ref = referenceQueue.poll()) != null) {
if (ref instanceof DemoGCTargetWeakReference) {
System.out.println("Object " + ((DemoGCTargetWeakReference) ref).identifier + " is garbage collected.");
}
}
}
}
In this example, weak references are linked with a queue. Once garbage collection occurs, the weak reference for the object is queued, enabling tracking of cleanup events. The lifecycle of objects held via weak references is entirely driven by the garbage collector, ensuring optimal memory utilization.