Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Java

Ví dụ Soft Reference và Phantom Reference trong Java

Java có 4 mức độ reference strength (mạnh → yếu): Strong → Soft → Weak → Phantom. GC xử lý khác nhau tùy mức độ. java.lang.ref package cung cấp các wrapper.

1. SoftReference – Cache nhạy cảm bộ nhớ

  • GC chỉ reclaim (thu hồi) soft-reachable objects khi JVM cần bộ nhớ (OOM imminent).
  • Thích hợp cho memory-sensitive cache (image cache, parsed data cache).
  • Có thể dùng với ReferenceQueue để cleanup sau khi GC clear.

Ví dụ SoftReference Cache

import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;

public class SoftReferenceCache {
    private final Map> cache = new HashMap<>();
    
    public void put(K key, V value) {
        cache.put(key, new SoftReference<>(value));
    }
    
    public V get(K key) {
        SoftReference ref = cache.get(key);
        if (ref == null) return null;
        V value = ref.get();
        if (value == null) {
            cache.remove(key); // GC đã clear
        }
        return value;
    }
    
    public static void main(String[] args) {
        SoftReferenceCache cache = new SoftReferenceCache<>();
        cache.put("key1", "Large data object 1");
        cache.put("key2", "Large data object 2");
        
        System.out.println("Before GC: " + cache.get("key1")); // Large data object 1
        
        // Giả lập memory pressure
        System.gc(); // Chỉ hint, không guarantee
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        
        System.out.println("After GC: " + cache.get("key1")); // Có thể null nếu GC reclaimed
    }
}

2. WeakReference – Canonicalizing mappings, Observer pattern

  • GC reclaim weak-reachable objects ở mọi GC cycle (không chờ memory pressure).
  • Dùng cho WeakHashMap (key weak), listener lists, canonicalizing maps.

Ví dụ WeakReference

import java.lang.ref.WeakReference;

public class WeakReferenceDemo {
    public static void main(String[] args) {
        Object obj = new Object() {
            @Override public String toString() { return "MyObject"; }
        };
        
        WeakReference weakRef = new WeakReference<>(obj);
        System.out.println("Before null: " + weakRef.get()); // MyObject
        
        obj = null; // Chỉ còn weak reference
        System.gc();
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        
        System.out.println("After GC: " + weakRef.get()); // null (đã bị GC reclaim)
    }
}

3. PhantomReference – Pre-mortem cleanup, flexible finalization

  • Phantom-reachable object: đã finalized (nếu có finalize), nhưng chưa bị reclaim.
  • phantomRef.get() luôn trả về null – không thể revive object.
  • BẮT BUỘC đăng ký với ReferenceQueue – khi object phantom-reachable, reference được enqueue.
  • Dùng cho: cleanup native resources, off-heap memory, thay thế finalize() (deprecated Java 9, removed Java 18).

Ví dụ PhantomReference (Sửa lỗi code gốc: get() trả null)

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class PhantomReferenceDemo {
    public static void main(String[] args) {
        ReferenceQueue queue = new ReferenceQueue<>();
        
        Object obj = new Object() {
            @Override protected void finalize() { System.out.println("finalize() called"); }
        };
        
        // Tạo phantom reference ĐĂNG KÝ VỚI QUEUE
        PhantomReference phantomRef = new PhantomReference<>(obj, queue);
        
        System.out.println("phantomRef.get() = " + phantomRef.get()); // LUÔN NULL
        
        obj = null; // Eligible for finalization + phantom reachable
        System.gc();
        
        try {
            // Chờ reference được enqueue (blocking)
            Reference ref = queue.remove(5000); // timeout 5s
            if (ref != null) {
                System.out.println("Object is phantom reachable! Performing cleanup...");
                // Cleanup native resource, off-heap memory, etc. tại ĐÂY
                // Object sẽ bị reclaim sau khi cleanup xong
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        // Note: Không thể gọi obj.method() ở đây vì obj đã null và get() trả null
    }
}

So sánh 4 loại Reference

Reference TypeGC Reclaim Timingget() trả vềReferenceQueueUse Case
StrongKhông bao giờ (trong khi reachable)ObjectKhôngNormal object reference
SoftKhi JVM cần memory (OOM imminent)Object hoặc nullOptionalMemory-sensitive cache
WeakNext GC cycleObject hoặc nullOptionalWeakHashMap, listeners, canonicalizing
PhantomSau finalization, trước reclaimLuôn nullBắt buộcPre-mortem cleanup, native resource release

Lưu ý quan trọng

  • finalize() deprecated (Java 9), removed (Java 18). Dùng PhantomReference + ReferenceQueue hoặc Cleaner (Java 9+).
  • Cleaner là high-level abstraction trên PhantomReference – dễ dùng hơn.
  • Reference objects bản thân cũng bị GC nếu không còn strong reference đến chúng.

Cleaner Example (Java 9+, khuyên dùng thay PhantomReference)

import java.lang.ref.Cleaner;

public class CleanerDemo {
    private static final Cleaner cleaner = Cleaner.create();
    
    static class Resource implements Runnable {
        private final int id;
        Resource(int id) { this.id = id; System.out.println("Resource " + id + " acquired"); }
        public void run() { System.out.println("Cleaning up resource " + id); }
    }
    
    public static void main(String[] args) {
        Resource res = new Resource(1);
        // Đăng ký cleanup action
        Cleaner.Cleanable cleanable = cleaner.register(res, res);
        
        res = null; // Eligible for GC
        System.gc();
        // Cleanup action sẽ chạy khi GC reclaim object
    }
}