weakhashmap 专用于防止键对象长期持有导致的内存泄漏,而非替代lru/ttl缓存;其弱引用仅对无强引用的键生效,需用new对象作key、重写equals/hashcode,value须轻量或弱化,且不线程安全,适用于瞬时对象元数据绑定场景。

WeakHashMap 本身不是为“长期缓存”设计的,它防的是因键对象被意外长期持有而导致的内存泄漏,而不是替代 LRU、TTL 等缓存策略。它的核心作用是:当键对象(如 UI 控件、临时 DTO、网络连接)在业务逻辑中已不再被使用时,不阻碍 GC 回收该键,从而自动清理整个条目——这恰恰能切断 HashMap 常见的“幽灵租客”式泄漏。
关键前提:键必须是可被回收的普通实例
WeakHashMap 的弱引用只对没有其他强引用的键对象生效。所以:
- 不能用字符串字面量(
"user_id")、枚举常量、静态 final 字段作 key——它们驻留在常量池或类静态区,永不回收 - 应使用 new 出来的轻量对象,例如:
new ButtonKey(buttonId)或new RequestContext(requestId) - 确保该 key 实例未被存入静态集合、ThreadLocal、监听器链等——否则强引用链会锁死回收
- 重写
equals()和hashCode(),否则弱引用失效后可能残留脏 Entry
值对象必须轻量,或主动弱化
WeakHashMap 只弱化 key,value 始终是强引用。若 value 是大字节数组、图片缩略图、预计算 JSON 等,即使 key 被回收,value 仍占内存且无法访问——这就是 Hutool WeakCache 泄漏的根源。
- 优先用不可变小对象作 value:String、Integer、Boolean、枚举等
- 若 value 确实较大,手动包装为
WeakReference<bigvalue></bigvalue>或SoftReference<bigvalue></bigvalue> - 绝对禁止 value 中直接持有 key 的强引用(比如 lambda 捕获 key、value 对象内含 key 字段)——会形成循环引用,key 永远不回收
线程安全与正确使用方式
WeakHashMap 本身非线程安全,且清理逻辑(expungeStaleEntries())在 get/put 时触发,带内部锁。高并发下不宜裸用。
- 低并发场景:用
Collections.synchronizedMap(new WeakHashMap()) - 中高并发场景:封装成同步类,读写方法加
synchronized或ReentrantLock - 不要在遍历中修改(如 for-each + remove),可能抛
ConcurrentModificationException - 避免将 WeakHashMap 当作通用映射表使用——GC 清理时机不可控,条目可能随时消失
典型适用场景举例
它真正起作用的地方,是元数据依附于瞬时对象的场景:
- JButton → tooltip 配置(按钮 dispose 后,配置自动消失)
- Socket 连接 → 上下文标签(连接 close 后,标签条目自动清理)
- Spring AOP 中临时代理对象 → 动态生成的序列化策略(代理销毁,策略随之释放)
- Android View → 自定义属性快照(View 被 destroy,快照不留痕)
这些场景共同点是:键生命周期由外部框架或业务流程控制,你不想、也不该手动调用 remove();WeakHashMap 提供了“零干预”的自动解耦。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











