weakhashmap 不适合直接做图片缓存,因其 key 弱引用而 value 强引用,entry 不会及时清理,易导致内存泄漏和 oom;需配合 softreference 包装 value 并手动清理失效 entry。

WeakHashMap 为什么不适合直接做图片缓存
WeakHashMap 的 key 是弱引用,value 却是强引用——这意味着只要 value(比如一张 Bitmap 或 BufferedImage)还被业务代码持有着,它就不会被回收,WeakHashMap 自身也**不会主动清理 entry**。你往里 put 一张图,哪怕 key 已经被 GC,entry 还卡在 map 里,直到下次 resize 或迭代时才被清理。这不是“自动释放”,而是“延迟清理”,对内存敏感的图片缓存来说不可靠。
常见错误现象:OutOfMemoryError: Java heap space 依然会发生,尤其在 Android 上反复加载大图、或桌面 Java 应用中缓存百张高分辨率图片时。
真正能缓解 OOM 的,不是 WeakHashMap 本身,而是它配合「value 也弱引用化」+「主动控制生命周期」的设计:
- 把图片对象包装进
WeakReference或SoftReference再存为 value - key 用能唯一标识图片的轻量对象(如
String url或自定义不可变 key),而非图片本身 - WeakHashMap 只负责管理 key 的可达性,不承担 value 回收责任
用 WeakHashMap + SoftReference 构建安全缓存的核心写法
SoftReference 比 WeakReference 更适合图片缓存:JVM 在内存不足时才会批量回收软引用对象,而弱引用可能刚离开作用域就被清掉,导致缓存命中率暴跌。
一个最小可行实现(以 Java 17+ 为例):
public class ImageCache {
private final Map<string softreference>> cache
= new WeakHashMap();
public BufferedImage get(String key) {
SoftReference<bufferedimage> ref = cache.get(key);
BufferedImage img = (ref != null) ? ref.get() : null;
if (img == null) {
cache.remove(key); // 清理失效 entry,避免堆积
}
return img;
}
public void put(String key, BufferedImage value) {
// 不直接存 value,而是包一层 SoftReference
cache.put(key, new SoftReference(value));
}
}</bufferedimage></string>
注意点:
- 每次
get()后必须判空并手动remove(),否则失效的SoftReference会持续占着 WeakHashMap 的桶位 - key 必须重写
equals()和hashCode()(String已满足;若用自定义 key,别忘了这步) - 不要在
put()前检查 key 是否存在——WeakHashMap 的get()本身不触发清理,旧 entry 可能残留
Android 场景下必须绕开的坑:Bitmap 与 WeakHashMap 的双重陷阱
在 Android 中直接缓存 Bitmap 到 WeakHashMap,极易出问题:
-
Bitmap对象本身小,但像素数据在 native heap(非 JVM heap),WeakHashMap 的 key 弱引用机制对 native 内存完全无效 - Android 8.0+ 默认启用
LargeHeap,但 native 内存仍受系统限制,OOM 往往先报Failed to allocate a 12345678 byte allocation -
WeakHashMap的哈希表结构在频繁增删时可能触发 rehash,产生临时对象,加剧 GC 压力
更稳妥的做法:
- 用
LruCache<string bitmap></string>(它内部已处理了 entry 清理和 size 计算) - 若坚持用 WeakHashMap,value 必须是
SoftReference<weakreference>></weakreference>?不,太重——改用Bitmap.createBitmap()后立即调用bitmap.recycle()配合监听生命周期 - 关键:缓存前压缩尺寸,例如用
BitmapFactory.Options.inSampleSize控制解码缩放,从源头减负
什么时候该放弃 WeakHashMap 改用其他方案
WeakHashMap 的适用边界很窄:仅当你需要「key 的生命周期天然短于 value,且 key 本身容易被意外强引用」时才值得用。图片缓存几乎不满足这个前提。
更推荐的替代路径:
- 纯内存缓存:用
Caffeine(支持大小限制、过期、引用策略),配置softValues():Caffeine.newBuilder().maximumSize(100).softValues().build() - 混合缓存:内存层用
LruCache(Android)或ConcurrentHashMap+ 定时淘汰,磁盘层用disklrucache或Room - 服务端场景:直接上 Redis,用
EXPIRE或 LRU 驱逐策略,比 JVM 级弱引用更可控
WeakHashMap 不是缓存工具,它是个「带弱键的哈希表」——把它当缓存用,等于拿螺丝刀当锤子。真要防 OOM,得从引用强度、容量上限、回收时机三个维度同时控制,而不是只盯着 key 弱不弱。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











