好的,下面是对 Java 集合系列第 13 篇:WeakHashMap
详解(含源码解析 + 使用示例) 的完整专业讲解。
✅ Java 集合系列13:WeakHashMap
详解(源码解析 + 使用示例)
🧠 一、什么是 WeakHashMap
?
WeakHashMap
是 Java 中的一种基于 弱引用(WeakReference) 实现的 Map,它的键对象只要不再被外部引用,就可以被 GC 回收,并自动从 Map 中移除对应的 Entry。
它适合用作:缓存、监听器注册表、元数据存储 等内存敏感场景。
📦 二、WeakHashMap 与 HashMap 的区别
对比项 | HashMap | WeakHashMap |
---|---|---|
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 的注意事项
- 只能在 Key 上弱引用,Value 是强引用(如果 Value 也要弱引用,需手动包装)。
- Key 必须没有其他强引用,否则无法被 GC。
WeakHashMap
是非线程安全的,不适合多线程直接使用。- 常用于缓存场景,如 Java Beans、图标缓存等,节省内存。
✅ 七、典型应用场景
应用场景 | 描述 |
---|---|
缓存系统 | 缓存对象只要不再被其他引用,可自动回收 |
元数据存储 | 不打扰主对象生命周期的元信息存储 |
事件监听器 | 避免监听器因注册未注销而造成内存泄漏 |
动态代理缓存 | 代理对象缓存,弱引用释放占用 |
📚 八、参考资料
- Java 官方文档 – WeakHashMap
- 《Effective Java》第三版:关于缓存与弱引用的讨论
- Java 源码分析:
OpenJDK
的 WeakHashMap 实现
发表回复