好的,下面是对 Java 集合系列第 13 篇:WeakHashMap 详解(含源码解析 + 使用示例) 的完整专业讲解。


✅ Java 集合系列13:WeakHashMap 详解(源码解析 + 使用示例)


🧠 一、什么是 WeakHashMap

WeakHashMap 是 Java 中的一种基于 弱引用(WeakReference) 实现的 Map,它的键对象只要不再被外部引用,就可以被 GC 回收,并自动从 Map 中移除对应的 Entry

它适合用作:缓存监听器注册表元数据存储 等内存敏感场景。


📦 二、WeakHashMap 与 HashMap 的区别

对比项HashMapWeakHashMap
Key 的引用强度强引用弱引用(java.lang.ref.WeakReference
GC 是否能回收键否(直到手动 remove)是,只要没有强引用
Entry 自动移除是,自动移除 key 被 GC 的条目
应用场景普通键值存储缓存、监听器、内存敏感数据

⚙️ 三、源码结构与核心机制解析

3.1 数据结构

public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
    private Entry<K,V>[] table;
    ...
}
  • Entry<K, V> 继承 WeakReference:也就是说,Key 是一个 WeakReference,而不是强引用。
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
    V value;
    Entry<K,V> next;
    int hash;
    ...
}

3.2 核心原理:ReferenceQueue + WeakReference

  • 当 key 的弱引用被 GC 回收后,JVM 会将其对应的 Entry 放入 ReferenceQueue
  • WeakHashMap 会在每次访问前清理该队列,移除无效 Entry(称为“自清理机制”)。

3.3 关键源码:自动清除逻辑

private void expungeStaleEntries() {
    Object r;
    while ((r = queue.poll()) != null) {
        synchronized (queue) {
            @SuppressWarnings("unchecked")
            Entry<K,V> e = (Entry<K,V>) r;
            int i = indexFor(e.hash, table.length);
            Entry<K,V> prev = table[i];
            Entry<K,V> p = prev;
            while (p != null) {
                Entry<K,V> next = p.next;
                if (p == e) {
                    if (prev == e)
                        table[i] = next;
                    else
                        prev.next = next;
                    size--;
                    break;
                }
                prev = p;
                p = next;
            }
        }
    }
}

解释:通过 ReferenceQueue.poll() 获取已被 GC 回收的 key 对应的 Entry,然后从哈希桶中移除它。


🧪 四、使用示例

示例:验证 WeakHashMap 中键对象被 GC 后自动删除

import java.util.*;

public class WeakHashMapDemo {
    public static void main(String[] args) throws InterruptedException {
        Map<Object, String> map = new WeakHashMap<>();
        Object key = new Object(); // 强引用对象
        map.put(key, "弱引用测试");

        System.out.println("插入后 map.size = " + map.size());

        key = null;  // 移除强引用

        System.gc(); // 提示 GC 回收
        Thread.sleep(1000); // 等待回收

        System.out.println("GC后 map.size = " + map.size());
    }
}

输出示例:

插入后 map.size = 1
GC后 map.size = 0

说明 WeakHashMap 成功地根据 GC 自动移除了无主键对象。


🧪 五、与其他 Map 的对比分析

Map 类型键引用类型是否自动删除无用 Entry应用场景
HashMap强引用普通数据存储
WeakHashMap弱引用缓存、监听器注册
ConcurrentHashMap强引用并发安全存储
IdentityHashMap强引用(==比对)使用对象地址判断相等
LinkedHashMap强引用否(可配 LRU)LRU 缓存等

🔍 六、使用 WeakHashMap 的注意事项

  1. 只能在 Key 上弱引用,Value 是强引用(如果 Value 也要弱引用,需手动包装)。
  2. Key 必须没有其他强引用,否则无法被 GC。
  3. WeakHashMap 是非线程安全的,不适合多线程直接使用。
  4. 常用于缓存场景,如 Java Beans、图标缓存等,节省内存。

✅ 七、典型应用场景

应用场景描述
缓存系统缓存对象只要不再被其他引用,可自动回收
元数据存储不打扰主对象生命周期的元信息存储
事件监听器避免监听器因注册未注销而造成内存泄漏
动态代理缓存代理对象缓存,弱引用释放占用

📚 八、参考资料