WeakHashMap 不能防止内存溢出,因其 value 是强引用;需配合 SoftReference 和手动清理,或用于 key 短命、value 轻量的场景,并定期调用 expungeStaleEntries()。

WeakHashMap 本身不能直接防止缓存导致的内存溢出,它只是“辅助工具”,关键在于怎么用。它的弱引用只作用于 key,而 value 仍是强引用——如果业务代码还拿着图片、对象或类加载器不放,value 就不会被回收,entry 也卡在 map 里,最终照样 OOM。
下面说清楚几个核心点,帮你避开常见坑:
WeakHashMap 的缓存角色其实是“自动清理 key 失效的条目”,不是“自动释放 value”
- 它只保证:当 key 没有其他强引用时,该 entry 后续会在 resize 或迭代时被清理(不是立刻);
- 但 value 如果是 BufferedImage、ClassLoader 或大对象,只要还有别的地方强引用着,它就一直占着堆内存;
- 更糟的是:WeakHashMap 不会主动调用
expungeStaleEntries(),得靠你触发(比如size()、entrySet()、put()等操作),否则失效 entry 堆积,table 膨胀,甚至引发线程数暴涨(如 jarLoader 场景中反复创建新 classloader)。
真正安全的图片/资源缓存,要组合 SoftReference + 主动清理
SoftReference 比 WeakReference 更适合缓存,因为 JVM 会在内存压力大时才回收它,保留一定命中率:
- 把 value 包一层
SoftReference存进 WeakHashMap; - 每次
get()后必须判空,为空就remove(key),避免 null 引用堆积; -
put()不直接存对象,而是存包装后的软引用; - 不依赖 GC 触发清理,自己控制生命周期。
示例精简写法:
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); // 必须手动清理
}
return img;
}
public void put(String key, BufferedImage value) {
cache.put(key, new SoftReference(value));
}</bufferedimage></string>
WeakHashMap 更适合做“生命周期绑定型缓存”,比如 session、view、activity 相关场景
- key 是短命对象(如 Activity、View、UserSession),你希望它一销毁,对应缓存就自然失效;
- value 是轻量对象(如配置、状态标记、监听器),不占多少内存;
- 这时 WeakHashMap 才真正发挥价值:不用手动 remove,GC 回收 key 后,下次访问自然拿不到,配合定期 expunge 就很干净。
比如:
private static final Map<view integer> clickCount = new WeakHashMap(); // View 销毁后,key 自动失效,不会泄漏 View 实例</view>
别把 WeakHashMap 当成“免维护缓存容器”
- 它不提供大小限制、LRU 淘汰、过期时间等能力;
- 如果缓存需要可控性(如最多存 100 张图),得自己加逻辑,或者换
LruCache(Android)或Caffeine(Java); - 在高并发或长周期服务中,建议搭配
ScheduledExecutorService定期调用expungeStaleEntries(),或封装成带清理机制的 wrapper 类。
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











