WeakHashMap能避免内存泄漏,因其键为弱引用,无强引用时GC可回收键并自动移除条目;而HashMap的强引用会阻止GC,导致对象长期驻留内存。

WeakHashMap 适合做轻量级缓存,因为它的键是弱引用的——只要没有强引用指向该键,垃圾回收器就能在下一次 GC 时回收它,同时 WeakHashMap 会自动移除对应的键值对。
为什么 WeakHashMap 能避免内存泄漏
普通 HashMap 的键是强引用,即使业务逻辑已不再需要某个键对象,只要它还在 Map 中,GC 就不会回收它。WeakHashMap 把键包装成 WeakReference,JVM 在 GC 时会清空所有仅被弱引用持有的键对象,并在后续访问(如 get、put)时触发清理过期条目(通过 expungeStaleEntries())。
注意:WeakHashMap 的值仍是强引用。如果值也持有对键的强引用(比如值是一个包含 key 字段的对象),就会形成循环引用,导致键无法被回收——这是常见陷阱。
构建缓存的基本写法
直接使用 WeakHashMap 即可,但需注意以下几点:
立即学习“Java免费学习笔记(深入)”;
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 键类型必须正确重写
equals()和hashCode(),否则弱引用失效后可能残留脏数据 - 不要用可变对象作键(如 ArrayList、StringBuilder),因为哈希值变化会导致 get 失败
- 不建议直接暴露 WeakHashMap 实例,应封装为线程安全的缓存类(尤其在多线程场景)
简单线程安全封装示例
若并发不高,可用 Collections.synchronizedMap 包装;若要求更高性能,可用 ConcurrentHashMap + 手动管理弱键逻辑(但注意 ConcurrentHashMap 不支持弱键,需自行结合 ReferenceQueue 实现)。更实用的做法是:
- 用 WeakHashMap 存储键值对
- 读写操作加同步块(或使用 ReentrantLock)
- 定期调用
cleanUp()(内部触发 expungeStaleEntries)或在每次 get/put 后清理
例如:
private final Mappublic synchronized Value get(Key key) {
return cache.get(key);
}
public synchronized void put(Key key, Value value) {
cache.put(key, value);
}
适用场景与局限性
WeakHashMap 缓存适用于:
– 键对象生命周期由外部控制(如 UI 组件、数据库连接、临时上下文对象)
– 缓存命中率不高,且允许随时丢失条目
– 对内存敏感,不能容忍“缓存常驻”导致 OOM
不适合:
– 需要精确控制缓存大小或过期时间(WeakHashMap 没有 LRU、TTL 等机制)
– 键对象本身是常量或静态对象(如 String 字面量、Integer 小值),它们被 JVM 缓存,永不回收
– 值对象很大且与键强关联(此时应考虑软引用或手动解耦)

















