WeakHashMap弱键自动回收

wen java案例 2

本文目录导读:

WeakHashMap弱键自动回收

  1. WeakHashMap弱键自动回收机制
  2. 自动回收触发条件
  3. 实际应用场景
  4. 关键注意事项
  5. 与普通 HashMap 对比
  6. 最佳实践

WeakHashMap弱键自动回收机制

WeakHashMap 是 Java 中的一种特殊 Map 实现,其键使用弱引用(WeakReference)包装,当键对象不再被外部强引用持有时,垃圾回收器会自动回收该键,并自动删除对应的条目。

核心原理

// 简化的内部实现
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
    V value;
    final int hash;
    Entry<K,V> next;
}

自动回收触发条件

键对象失去强引用

public class WeakHashMapDemo {
    public static void main(String[] args) {
        WeakHashMap<KeyObject, String> map = new WeakHashMap<>();
        // 创建强引用
        KeyObject key = new KeyObject("key1");
        map.put(key, "value1");
        // key仍然有强引用,不会回收
        System.out.println(map.size()); // 1
        key = null; // 移除强引用
        // 此时只有 WeakHashMap 中的弱引用持有该键
        // GC 后会被回收
        System.gc();
        System.out.println(map.size()); // 0(可能为0)
    }
}

GC 执行时自动清理

当垃圾回收器运行时,会通过 ReferenceQueue 机制检测弱引用是否被回收:

// WeakHashMap 内部的 ReferenceQueue
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
// 清理方法,在每次操作时调用
private void expungeStaleEntries() {
    for (Object x; (x = queue.poll()) != null; ) {
        synchronized (queue) {
            Entry<K,V> e = (Entry<K,V>) x;
            int i = indexFor(e.hash, table.length);
            // 从哈希表中移除该条目
            // ...
        }
    }
}

实际应用场景

缓存系统

public class ImageCache {
    private WeakHashMap<ImageKey, BufferedImage> cache = new WeakHashMap<>();
    public BufferedImage getImage(ImageKey key) {
        BufferedImage img = cache.get(key);
        if (img == null) {
            img = loadFromDisk(key);
            cache.put(key, img);
        }
        return img;
    }
}

元数据存储

public class MetadataManager {
    private WeakHashMap<Object, Metadata> metadataMap = new WeakHashMap<>();
    public void setMetadata(Object obj, Metadata meta) {
        metadataMap.put(obj, meta);
    }
    public Metadata getMetadata(Object obj) {
        return metadataMap.get(obj);
    }
    // 当对象不再使用时,元数据自动清理
}

监听器/回调注册

public class EventManager {
    private WeakHashMap<Listener, Void> listeners = new WeakHashMap<>();
    public void register(Listener listener) {
        listeners.put(listener, null);
    }
    // 监听器对象不再使用时自动移除,避免内存泄漏
}

关键注意事项

值对象不会自动回收

WeakHashMap<Key, HeavyObject> map = new WeakHashMap<>();
map.put(key, new HeavyObject()); // 即使key被回收,value不会被回收!
// 解决办法:值也使用弱引用包装
WeakHashMap<Key, WeakReference<HeavyObject>> safeMap = new WeakHashMap<>();

String 作为键的特殊情况

// String.intern() 返回的字符串有强引用
String s1 = "abc"; // 字符串常量池中的强引用
String s2 = new String("abc"); // 堆上的对象
map.put(s2, "value"); // s2可以被回收
map.put(s1, "value"); // 不可被回收

自动回收的时机不确定

// 不要依赖立即回收
map.put(key, "value");
key = null;
System.gc();
// 此时可能尚未回收
Thread.sleep(100);
// 建议调用 size() 等方法触发清理
map.size(); // 内部会调用 expungeStaleEntries()

与普通 HashMap 对比

特性 WeakHashMap HashMap
键引用类型 弱引用 强引用
自动回收 支持 不支持
内存泄漏风险 高(需手动移除)
使用场景 缓存、元数据 大多数场景

最佳实践

  1. 键必须是长生命周期的对象,否则很快会被回收
  2. 避免在并发场景下使用(非线程安全)
  3. 不适合存储短暂使用的键值对
  4. 结合 ReferenceQueue 使用可以监听回收事件
// 监听回收事件示例
ReferenceQueue<Object> queue = new ReferenceQueue<>();
WeakHashMap<Object, String> map = new WeakHashMap<>();
// 可以另起线程监控回收
new Thread(() -> {
    try {
        while (true) {
            Reference<? extends Object> ref = queue.remove();
            System.out.println("键被回收: " + ref);
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}).start();

WeakHashMap 的弱键自动回收机制在需要自动清理不用的条目时非常有用,特别适合缓存、元数据管理、监听器注册等场景,但需要理解其工作原理和限制,避免误用导致的问题。

抱歉,评论功能暂时关闭!