weakhashmap通过弱引用实现键的自动释放,核心是将键包装为weakreference并配合referencequeue惰性清理:gc回收键后entry暂留table中(键为null),后续get/put等操作调用expungestaleentries批量清除。

WeakHashMap 通过弱引用自动释放键值,核心在于它把键包装成 WeakReference,并配合 ReferenceQueue 实现惰性清理——不是实时删除,而是在后续操作中触发回收检查。
键被包装为 WeakReference,不阻止 GC
WeakHashMap 的每个 Entry 都继承自 WeakReference<object></object>,实际持有着对键对象的弱引用。只要外部不再有强引用指向该键,JVM 在任意一次 GC 中都可能回收它,哪怕堆内存还很充足。这个过程完全由 JVM 控制,WeakHashMap 本身不干预。
- 键对象一旦仅剩 WeakHashMap 中这一个弱引用,就成为 GC 的候选目标
- 值对象仍被强引用持有,是否回收取决于外部是否还引用该值
- 若值对象内部又强引用了键(如匿名内部类捕获 key),会形成循环引用,导致键永远无法回收
回收后登记到 ReferenceQueue,等待清理
当 GC 回收某个键时,JVM 会自动将对应 Entry 所关联的 WeakReference 实例加入 WeakHashMap 内部维护的 ReferenceQueue。此时 Entry 仍存在于底层哈希表数组(table)中,只是键字段已为 null,处于“待清理”状态。
- queue 中存放的是已被回收键所对应的 WeakReference 实例
- 这个队列是 GC 与 WeakHashMap 之间唯一的通信通道
- Entry 本身不会立刻从 table 中移除,避免每次 GC 都同步开销
访问时惰性调用 expungeStaleEntries 清理
真正删除 Entry 发生在下一次调用 get()、put()、size()、entrySet() 等方法时。这些方法开头都会先执行 expungeStaleEntries(),遍历 queue 并从 table 中摘除所有已失效 Entry。
- 清理是批量进行的:一次遍历 queue,清除所有已回收键的条目
- table 中空槽位不会立即压缩,但后续 resize 或遍历时会自然跳过
- 因此
size()返回的是当前可达键的数量,不是精确实时值
不访问就不会清理?是的,但影响有限
如果 WeakHashMap 创建后一直未被访问,且期间发生了 GC,那些已回收键对应的 Entry 会滞留在 table 中,占用少量内存和哈希桶位置。但这种场景极少构成实际压力:
- 多数 WeakHashMap 用于监听器注册、临时元数据缓存等,天然伴随频繁读写
- 即使长期闲置,Entry 本身只含 null 键 + 强引用值,空间开销小
- 一旦开始使用,首次访问就会清空全部陈旧项
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











