WeakHashMap 的弱引用键回收依赖 JVM GC 与 ReferenceQueue 协同:Entry 继承 WeakReference,key 为 referent;GC 回收 key 后将其 Entry 入队;expungeStaleEntries() 在 get/put 等操作中懒清理队列 Entry 并置空 value;常量池对象因被 JVM 强引用而永不入队,弱引用失效。

要剖析 WeakHashMap 的弱引用键回收机制,关键不是“看它怎么写”,而是理解它如何与 JVM 垃圾回收器协同工作——WeakHashMap 本身不决定何时回收,只被动响应 GC 的结果。
看 Entry 如何继承 WeakReference
WeakHashMap 的内部 Entry 类(WeakHashMap.Entry)直接继承自 WeakReference<Object>,且把 key 作为 referent 传入父类构造器。这意味着:
- 每个 Entry 本质上就是一个包装了 key 的弱引用对象
- key 不再是 Entry 的普通字段,而是由 WeakReference 管理的 referent
- value 仍是强引用字段,不受弱性保护
跟踪 ReferenceQueue 的联动逻辑
WeakHashMap 初始化时会创建一个私有 ReferenceQueue<Object>(叫 queue),所有 Entry 都注册到这个队列。当 GC 发现某个 key 只被 WeakReference 持有时,就会:
- 回收该 key 对象
- 将对应 Entry 加入
queue - 但 Entry 本身仍留在哈希表数组中,未被删除
观察 expungeStaleEntries() 的触发时机
真正清理失效 Entry 的动作发生在 expungeStaleEntries() 方法里,而它**不是定时运行,也不是后台线程调用**,而是被以下方法显式前置调用:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
-
get()、put()、remove() -
size()、isEmpty() -
keySet()、entrySet()、values()的首次访问
该方法会循环从 queue.poll() 取出已入队的 Entry,并从哈希表中移除对应槽位,同时置空 value 引用(防止内存泄漏)。
验证常量池对象为何“不弱”
用 "abc" 或 Integer.valueOf(100) 作 key 时,源码层面看不出异常,但调试时会发现:
- 这些对象被 JVM 全局强引用(如
StringTable、IntegerCache.cache)持有 - GC Roots 可达,始终不满足“仅被弱引用持有”的条件
- 对应 Entry 永远不会进 queue,
expungeStaleEntries()也清不掉
所以实际效果等同于普通 HashMap —— 弱引用机制完全失效。
不复杂但容易忽略:WeakHashMap 的“自动”清理,本质是一套依赖 GC + 队列 + 懒清理的组合机制,源码里看不到主动回收逻辑,只有被动响应链条。

















