weakhashmap 仅适用于键为业务中已存在强引用对象的缓存场景,其键为弱引用、值为强引用,gc时仅当key无其他强引用时才被清理,不支持过期、容量控制等通用缓存功能。

Java 中的 WeakHashMap 可以用于构建“随 GC 自动释放”的缓存,但需注意:它**不是通用缓存解决方案**,仅适用于特定场景——即缓存键(key)本身已是业务中其他地方强引用的对象,且你希望当这些对象不再被强引用时,对应缓存条目能被自动清理。
WeakHashMap 的工作原理与适用前提
WeakHashMap 的键是弱引用(WeakReference),值仍是强引用。GC 时,若某 key 对象仅被 WeakHashMap 中的弱引用持有着,该 key-value 映射就会被移除(实际在下一次调用 get/put 等方法时惰性清理)。这意味着:
- 缓存是否失效,取决于 key 是否还被外部强引用,和 value 无关;
- value 不会阻止 key 被回收,但 value 本身若持有对 key 的强引用(如内部类、闭包等),会造成内存泄漏,key 永远无法被回收;
- 它不提供过期、容量限制、LRU 排除等缓存常用能力;
- 不适合用字符串字面量、常量池对象或长期存活单例作 key(它们几乎永不被回收)。
典型安全使用方式:以实例对象为 key
最常见且安全的用法,是将当前业务中已存在的、生命周期明确的 Java 对象(如 DAO 实例、DTO、临时计算上下文)作为 key:
- 例如:缓存某个用户会话对象(
UserSession)的权限树,key 就是该UserSession实例; - 当用户登出、会话超时、或页面关闭导致该实例不再被任何地方强引用时,下次 GC 后,
WeakHashMap中对应条目自动消失; - 确保 value 不反向持有 key(比如不要在 value 中存 this 或 lambda 引用 key);
- 避免用
new String("abc")这类可被 intern 的字符串作 key —— 即使 new 出来,也可能因 JVM 优化意外驻留。
基础代码示例(无额外封装)
以下是一个轻量、线程不安全但清晰的示意:
// 缓存:UserSession → 权限列表(假设 PermissionTree 是不可变或只读)
private final WeakHashMap<usersession permissiontree> sessionCache = new WeakHashMap();
public PermissionTree getPermissions(UserSession session) {
// 先查缓存
PermissionTree cached = sessionCache.get(session);
if (cached != null) {
return cached;
}
// 未命中,计算并缓存
PermissionTree tree = computePermissionTree(session);
sessionCache.put(session, tree); // key 是弱引用,value 是强引用
return tree;
}
</usersession>
注意:WeakHashMap 本身不是线程安全的。若多线程并发访问,应加锁(如 synchronized 方法块)、或包装为 Collections.synchronizedMap(但同步开销大,且不能解决迭代时的结构性变化问题)。
更稳妥的替代建议:用软引用 + 显式管理
如果目标是“尽量延长缓存存活时间,直到内存紧张”,WeakHashMap 并不合适(它太“弱”了,GC 一来就清)。此时应考虑:
- 使用
SoftReference<v></v>包装 value,并配合普通HashMap<k softreference>></k>; - 每次
get时检查SoftReference.get()是否为null,为空则重建; - 定期清理已失效的软引用条目(如在 put 前遍历清理,或用
ReferenceQueue回收监听); - 生产环境强烈推荐使用成熟库,如
Caffeine(支持 weakKeys/weakValues/softValues/maximumSize/expiry 等完整策略)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











