WeakHashMap 的弱引用键回收机制依赖 JVM GC 与 ReferenceQueue 协同:Entry 继承 WeakReference,key 为 referent;GC 回收 key 后将其 Entry 入队;expungeStaleEntries() 在 get/put/size 等操作中懒清理队列 Entry 并置空 value;常量池对象因被 JVM 强引用而永不入队,弱引用失效。

WeakHashMap 的弱引用键特性,核心在于“键的生命周期不由 Map 控制,而由 JVM 垃圾回收器决定”。它不是给缓存加一个定时删除功能,而是让缓存条目随键对象的自然消亡而自动失效——只要外部不再强持有那个 key,GC 就可能回收它,随后 WeakHashMap 在下次操作时顺势清理整条记录。
键被回收 ≠ 立即从表中消失
WeakHashMap 不会主动扫描或后台清理。它的清理动作(expungeStaleEntries())只在调用 get、put、size、keySet 等方法前触发。这意味着:
- 即使 GC 已回收 key,Entry 仍可能暂时留在内部数组中,size() 可能虚高
- 遍历 entrySet() 时,需手动检查 entry.getKey() != null,否则可能拿到已失效的 Entry
- System.gc() 仅是提示,不保证执行,生产环境通常禁用
键必须是可被 GC 的普通对象
WeakHashMap 对键的弱引用机制,只有在键本身能被回收时才生效。以下情况会让弱引用“失效”:
- 字符串字面量(如 "abc"):驻留在字符串常量池,有强引用长期存活
- 小整数包装类(如 Integer.valueOf(123)):来自 IntegerCache,同样被静态引用持有
- 静态变量、单例、全局缓存中持有的对象:只要还有强引用,就不会被回收
安全做法是使用运行时构造的对象作键,例如:new String("abc")、new UserKey(id)、int[]、UI 组件实例 等。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
值仍是强引用,小心循环引用导致内存滞留
WeakHashMap 只弱化键,**值(value)始终是强引用**。如果 value 反向持有 key(比如 value 是 key 的内部类、lambda 表达式、或显式保存了 key 引用),就会形成强引用闭环,使 key 实际无法被 GC 回收,整个 Entry 卡住不释放。
- 典型翻车场景:以某个 RequestContext 对象为 key,value 是一个 Handler 或 Listener,而该 Listener 内部又持有了这个 context
- 缓解方式:value 中避免强持 key;若必须关联,改用 WeakReference<K> 包装 key
- 注意:value 本身是否能被回收,完全取决于外部是否还有其他强引用,WeakHashMap 不干预
适合什么场景?不适合什么场景?
WeakHashMap 不是通用缓存方案,而是面向“生命周期依附型”关系的轻量工具:
- ✅ 适合:监听器注册表(以 Listener 实例为键)、UI 组件元数据缓存(以 View 为键)、请求上下文中间结果(以临时 Request 对象为键)
- ❌ 不适合:TTL 缓存(无时间控制)、LRU 缓存(无访问顺序)、高一致性要求场景(get 返回 null 不代表没存过)、多线程高频并发读写(非线程安全,需额外同步)
- ? 更优替代:Caffeine 或 Guava Cache,支持弱键/弱值/软引用/TTL/LRU/线程安全,API 更健壮

















