WeakHashMap不能平滑解决缓存内存泄漏,因其仅弱化key、value仍强引用,且清理惰性;真正安全方案是选用Caffeine/Guava Cache,或自建ReferenceQueue结构。

WeakHashMap 并不能“平滑解决”缓存场景下的内存泄漏——它只解决其中一半问题,且自带新陷阱。真正平滑的方案,是理解它的边界、规避它的缺陷,并在必要时换掉它。
WeakHashMap 只管 key,不管 value
WeakHashMap 的 key 是弱引用,GC 一来,没强引用的 key 就被清掉,对应 entry 也会被标记为 stale(失效)。但 value 仍是强引用,只要 value 还活着,它占的内存就一直留着。
常见踩坑点:
- value 是内部类实例,隐式持有外部对象(比如 Activity、Service),导致外部对象无法回收
- value 包含对 key 的反向引用(如 value 是一个包装器,持有了 key 的引用),形成循环引用,key 卡在 finalize 队列里出不来
- value 引用了大对象(如 byte[]、Bitmap、大型 DTO),哪怕 key 已消失,value 还在堆里白占空间
清理不是实时的,而是“懒惰触发”
WeakHashMap 不会在 key 被 GC 后立刻删掉 entry。它只在调用 get()、put()、size() 等方法时,顺手清理一部分 stale entry。这意味着:
立即学习“Java免费学习笔记(深入)”;
- 长期只读不写的缓存,stale entry 会越积越多,size() 返回值虚高,内存实际未释放
- 遍历时可能抛 ConcurrentModificationException,因为 GC 在中间清掉了 entry,而迭代器没感知
- 不能依赖 size() == 0 判断缓存是否为空——可能全是 stale entry,看着空,实则内存没腾
真正安全的缓存替代方案
除非你明确需要“key 消失 → 映射自动失效”且拒绝任何额外线程或回调,否则 WeakHashMap 不该是首选。
- Caffeine:JDK 8+ 主流选择,支持 weakKeys()/weakValues()/softValues()、最大容量、过期时间、移除监听(removalListener),清理及时、线程安全、性能优异
- Guava Cache:成熟稳定,提供类似能力,适合对兼容性要求高的老项目
- 自建 ReferenceQueue + WeakReference 结构:若需完全控制(比如绑定生命周期、触发回调),可配合 ReferenceQueue 手动监听 key 回收并同步清理 value
- 静态缓存 + 显式清理:对生命周期清晰的对象(如单次请求上下文),用 HashMap + clear() 或 remove(key) 更可控,避免弱引用语义带来的不确定性
如果非用 WeakHashMap,必须加防护层
仅当业务约束极严(如不能引入第三方库、不允许后台线程、必须零配置自动失效)时才保留 WeakHashMap,但要补足短板:
- value 必须是轻量对象,禁止持有外部引用或大内存结构
- 关键路径后手动触发清理:expungeStaleEntries()(需反射调用,因它是 package-private)
- 遍历前先转成快照:new ArrayList(map.entrySet()),避免并发异常,但注意这会短暂延长 value 存活
- 绝不拿常量字符串、Integer 缓存值等不可回收对象作 key —— 它们永驻常量池,WeakHashMap 形同虚设


















