javaadvanced

Weak, Soft, and Phantom References

Use Java reference types for memory-efficient caches, resource cleanup, and GC-aware data structures.

java
import java.lang.ref.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ReferenceDemo {

    // Soft reference cache — evicted only under memory pressure
    static class SoftCache<K, V> {
        private final Map<K, SoftReference<V>> cache = new ConcurrentHashMap<>();

        public void put(K key, V value) {
            cache.put(key, new SoftReference<>(value));
        }

        public V get(K key) {
            SoftReference<V> ref = cache.get(key);
            if (ref == null) return null;
            V value = ref.get();
            if (value == null) cache.remove(key);
            return value;
        }

        public void cleanup() {
            cache.entrySet().removeIf(e -> e.getValue().get() == null);
        }

        public int size() {
            cleanup();
            return cache.size();
        }
    }

    // Weak reference map — entries removed when key is GC'd
    static class WeakRegistry<K, V> {
        private final WeakHashMap<K, V> registry = new WeakHashMap<>();

        public void register(K key, V value) {
            registry.put(key, value);
        }

        public V lookup(K key) {
            return registry.get(key);
        }

        public int size() { return registry.size(); }
    }

    // Phantom reference for cleanup tracking
    static class CleanupTracker {
        private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
        private final Map<PhantomReference<?>, String> refs = new HashMap<>();

        void track(Object obj, String label) {
            PhantomReference<Object> phantom = new PhantomReference<>(obj, queue);
            refs.put(phantom, label);
        }

        void processCollected() {
            Reference<?> ref;
            while ((ref = queue.poll()) != null) {
                String label = refs.remove(ref);
                System.out.println("Collected: " + label);
                ref.clear();
            }
        }
    }

    public static void main(String[] args) {
        // Soft cache
        SoftCache<String, byte[]> cache = new SoftCache<>();
        cache.put("img1", new byte[1024 * 1024]); // 1MB
        cache.put("img2", new byte[1024 * 1024]);
        System.out.println("Cache size: " + cache.size()); // 2
        System.out.println("img1: " + (cache.get("img1") != null)); // true

        // Weak registry
        WeakRegistry<Object, String> registry = new WeakRegistry<>();
        Object key = new Object();
        registry.register(key, "my-resource");
        System.out.println("Before: " + registry.size()); // 1
        key = null;
        System.gc();
        System.out.println("After GC: " + registry.size()); // likely 0

        // Phantom tracking
        CleanupTracker tracker = new CleanupTracker();
        Object resource = new Object();
        tracker.track(resource, "db-connection-1");
        resource = null;
        System.gc();
        tracker.processCollected(); // Collected: db-connection-1
    }
}

Use Cases

  • Memory-sensitive caching that adapts to JVM pressure
  • Tracking object lifecycle and cleanup
  • Avoiding memory leaks in long-lived registries

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.