当前位置: 首页 > news >正文

WeakHashMap 学习

一、什么是 WeakHashMap?

WeakHashMap<K, V>是 Java 标准库(java.util包)中的一种特殊Map实现。它的核心特性是:

键(Key)被包装为弱引用(WeakReference),当某个键对象不再被任何强引用持有时,即使它还在WeakHashMap中,也会在下一次垃圾回收(GC)时被自动移除。

这使得WeakHashMap成为一种自动清理的 Map,非常适合用于缓存或元数据映射等场景。

二、WeakHashMap 的基本使用

示例代码:观察自动清理行为

import java.util.WeakHashMap; public class WeakHashMapDemo { public static void main(String[] args) { WeakHashMap<Key, String> map = new WeakHashMap<>(); Key key = new Key("obj1"); map.put(key, "value1"); System.out.println("Before GC: " + map.size()); // 输出 1 key = null; // 移除唯一强引用 // 建议触发 GC(不保证立即执行) System.gc(); try { Thread.sleep(100); } catch (InterruptedException e) {} // 调用 size() 会触发 expungeStaleEntries() System.out.println("After GC: " + map.size()); // 通常输出 0 } static class Key { private final String name; public Key(String name) { this.name = name; } @Override public String toString() { return name; } } }

⚠️ 注意:System.gc()只是建议 JVM 执行 GC,实际是否回收取决于 JVM 实现和运行时状态。但只要 GC 发生,且 key 无强引用,条目就会被清除。

三、适用场景(Where to Use)

✅ 推荐场景:

  1. 对象元数据缓存(Metadata Cache)
    例如:为每个对象关联一个调试信息、监听器列表、临时配置等,但不希望这些元数据阻止对象被回收。

  2. 避免内存泄漏的监听器注册表
    在事件驱动系统中,若监听器未显式注销,可能造成内存泄漏。使用WeakHashMap<Listener, ?>可让监听器在无其他引用时自动移除。

  3. ClassLoader 或 Class 相关的缓存
    比如动态代理类、反射元数据缓存等,避免因缓存导致 ClassLoader 无法卸载。

❌ 不适合场景:

  • 需要长期保留数据的缓存(如用户会话、热点数据)→ 应使用SoftReference(软引用)或成熟缓存框架(如 Caffeine、Guava Cache)。
  • 键是基本类型包装类(如Integer,String)且来自常量池 → 因为它们可能被 JVM 强引用(如字符串字面量),导致无法回收。

四、WeakHashMap 的优缺点

优点缺点
✅ 自动清理无用条目,防止内存泄漏❌ 条目可能随时消失,不适合需要稳定存储的场景
✅ 与 GC 协同工作,无需手动管理❌ 清理时机不可控(依赖 GC)
✅ 内存友好,适合辅助性缓存❌ 性能略低于 HashMap(每次操作需清理 stale entries)
✅ 线程不安全(与 HashMap 一致)❌ 不支持 null key(因为WeakReference(null)无意义)

补充:WeakHashMap不允许null作为 key,但允许null作为 value。


五、底层原理(How It Works)

1. 弱引用(WeakReference)

WeakHashMap内部使用WeakReference包装每个 key:

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { V value; // ... }
  • 当 key 对象仅被WeakReference引用时,JVM 认为其“可回收”。
  • GC 时,JVM 会将该WeakReference加入其关联的ReferenceQueue

2. ReferenceQueue 机制

WeakHashMap构造时会创建一个ReferenceQueue

private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
  • 当 key 被 GC 回收后,对应的Entry会被放入此队列。
  • WeakHashMap每次访问(get/put/size 等)时,会调用expungeStaleEntries()方法,遍历队列并删除已失效的条目。

这就是为什么map.size()在 GC 后返回 0 —— 因为size()触发了清理。

3. 清理不是实时的!

⚠️关键点WeakHashMap不会在 GC 后立即清理,而是在下次调用其方法时才清理。因此:

  • 如果长时间不访问 map,stale entries 会堆积(但不占用 key 对象内存,只占 Entry 对象内存)。
  • 它不是“实时缓存”,而是“懒清理”。

六、与 JVM 的关系

WeakHashMap的行为高度依赖 JVM 的垃圾回收机制引用类型语义

引用类型是否阻止 GCWeakHashMap 使用
强引用(Strong)✅ 是不使用
软引用(Soft)✅(直到内存不足)SoftReference,用于内存敏感缓存
弱引用(Weak)❌ 否WeakHashMap 的 key
虚引用(Phantom)❌ 否用于跟踪对象回收(不用于 Map)

因此,WeakHashMap的“自动清理”能力完全由 JVM 的 GC 决定,属于JVM 与 Java 语言协同设计的典型范例

七、生产环境是否使用?如何使用?

生产环境可以使用,但需谨慎!

正确使用姿势:
  1. 明确目的:仅用于“辅助性、可丢失”的元数据映射。
  2. 避免依赖清理时机:不要假设条目何时消失。
  3. 不要用于主业务数据存储
  4. 注意 key 的生命周期:确保 key 确实会变成“仅被 WeakHashMap 引用”。
生产级示例:监听器注册表(防内存泄漏)
import java.util.*; public class EventManager { // 使用 WeakHashMap 避免监听器泄漏 private final Map<Listener, Boolean> listeners = new WeakHashMap<>(); public void addListener(Listener listener) { listeners.put(listener, Boolean.TRUE); } public void fireEvent(String event) { // 清理 stale entries 并通知有效监听器 for (Listener l : new ArrayList<>(listeners.keySet())) { if (l != null) { l.onEvent(event); } } } interface Listener { void onEvent(String event); } }

即使调用方忘记removeListener,只要监听器对象无其他强引用,就会被自动清理。

八、替代方案对比

方案特点适用场景
WeakHashMapkey 弱引用,GC 后自动移除元数据、监听器、临时关联
SoftReference+ Mapkey 软引用,内存不足时才回收内存敏感缓存(如图片缓存)
Guava Cache / Caffeine支持 LRU、TTL、软/弱引用等通用缓存,功能强大
IdentityHashMap基于 == 比较特殊场景(如防止重写 equals 的干扰)

一般建议:除非明确需要弱引用语义,否则优先使用成熟缓存框架

九、总结

维度说明
本质key 为弱引用的 Map
核心机制依赖 JVM GC + ReferenceQueue
自动清理在下次访问时触发(懒清理)
线程安全❌ 不安全(需外部同步)
生产可用✅ 但仅限特定场景(元数据、防泄漏)
与 JVM 关系深度依赖 GC 行为和引用语义

💡一句话口诀
WeakHashMap 存的是‘影子’,主身一走,影子就散。

http://www.cnnetsun.cn/news/42099.html

相关文章:

  • 全网最全的软件测试面试八股文(含真题答案+文档)
  • OpenResume专业简历制作工具完整使用指南
  • springboot肿瘤患者康复回访系统_109a2sb0-
  • 【KL 散度】深入理解 Kullback-Leibler Divergence:AI 如何衡量“像不像”的问题
  • 5分钟掌握LIBERO:开启终身机器人学习的革命性平台
  • 文件上传革命:jQuery File Upload如何让开发效率飙升500%
  • SolidWorks三维模型与工程图差距分析介绍
  • COMSOL模拟锌离子电池锌负极电场模型教程:从零开始构建并详细解析源文件,适合初学者的电场建模教学
  • 终极指南:如何用PIKE-RAG打造领域专属的智能问答系统
  • 5分钟从文档小白到OCR专家:Zerox如何让文字识别变得像拍照一样简单
  • RocketMQ如何防止消息丢失?
  • CSS尺寸、盒子模型、定位、浮动与布局(Flex/Grid)
  • 《构建游戏实时流失预警模型的核心逻辑》
  • 两个步骤,打包war,tomcat使用war包
  • idea修改maven的刷新引入依赖快捷键
  • 纯电动汽车Simulink仿真模型建模详细步骤。 通过文档的形式,跟着文档一步一步操作,既可以...
  • 同花顺平衡多空看图操作多空理论
  • 通达信222222测试帖别下载
  • 通达信大盘个股共振指标公式
  • 这些核心特征,让芯片散料转编带设备成行业刚需
  • ~给媳妇的新称呼~
  • java计算机毕业设计社区服务微信小程序 基于微信生态的社区便民服务平台 SpringBoot+微信小程智慧社区服务系统
  • SynthPose-VitPose终极部署指南:从零到精通的人体姿态估计实战
  • DataEase vs PowerBI:当数据分析遇见选择困难症,你该如何破局?
  • android 之 AAudio
  • anoconda简单操作
  • 多场景头盔佩戴检测
  • 70看看:AI如何帮你快速生成代码项目
  • 13、Puppet 模块与类:从基础到高级应用
  • JBoltAI 识图阅卷:AI 赋能教育考评,开启智能阅卷新时代