weakhashmap 的弱引用键回收机制依赖 jvm gc 与 referencequeue 协同:entry 继承 weakreference,key 为 referent;gc 回收 key 后将其 entry 入队;expungestaleentries() 在 get/put 等操作中懒清理队列 entry 并置空 value;常量池对象因被 jvm 强引用而永不入队,弱引用失效。

WeakHashMap 本身不“配合”弱引用,它内部就用弱引用管理键——自动清理不是额外配置,而是它的底层行为。
键被包装成 WeakReference,GC 回收后进队列
WeakHashMap 把每个键对象包装成 WeakReference
- 字符串字面量(如 "abc")、Integer 小值(-128~127)、Spring 单例 Bean 等,因常量池或容器持有强引用,永远不会被回收,不适合作为键
- 推荐使用临时对象作键:RequestContext、View 实例、自定义 PermissionScope、DTO 对象等
清理不主动发生,只在操作时“顺手扫一扫”
WeakHashMap 不开线程、不轮询、不延时执行。它的清理逻辑封装在私有方法 expungeStaleEntries() 中,但这个方法只在你调用以下方法前被触发:
- put()、get():插入或查找前先清垃圾,避免哈希冲突或返回 null
- size()、isEmpty()、keySet()、entrySet():这些只读操作也会触发清理,否则 size 可能虚高
没调用,就不会清理——这是它轻量又“懒”的关键。
值仍是强引用,注意反向持有导致“失弱”
WeakHashMap 只对键做弱引用,value 默认是强引用。如果 value 隐式持有了 key(比如匿名内部类、Lambda、Handler、setTag、监听器回调),就会形成引用链,使 key 无法被 GC,缓存彻底失效。
- 常见泄漏场景:WeakHashMap
,但 Bitmap 内部通过 setTag 持有 View - 解决方案:value 也用 WeakReference
包装(尤其大对象),或确保 value 不捕获 key
适合什么场景,不适合什么
它解决的是“生命周期绑定”问题,不是通用缓存方案:
- 适合:图片缩略图缓存(以原始 Image 为键)、监听器注册表(以 Listener 实例为键)、请求上下文权限缓存(以 RequestContext 为键)
- 不适合:需要 TTL(过期时间)、LRU(访问顺序淘汰)、稳定存活保障的场景;此时应选 Caffeine、Ehcache 或 LinkedHashMap + 定时清理
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











