bigywater.tech
Java 内部原理 · 19

WeakHashMap:真实测出 GC 之后 10000 个 key 真的清空成了 0 个

WeakHashMap 常被介绍成"key 没人强引用就会自动从 map 里消失",但很少有人真实验证过这句话——包括它到底是"自动"到什么程度,要不要额外调用什么方法才能看到清理结果。

10000 → 0
真实测出:10000 个没有其它强引用的 key,GC 之后第一次调用 size() 就真实归零,不需要额外触发清理
10000 → 200
对照组故意保留 200 个 key 的强引用,GC 之后 size() 真实精确剩下 200,一个不多一个不少

上一篇:finalize() 真的能让对象复活:真实验证,但 WeakReference 不会跟着活过来

思路:key 是弱引用,但清理时机要靠真实测才知道

WeakHashMap 的每个 key 内部都是用 WeakReference 包起来的,GC 能回收只剩弱引用的 key——但"能回收"和"map.size() 什么时候真实反映出来"是两件事。

WeakHashMapDemo.java(核心逻辑) WeakHashMap<Object, String> map = new WeakHashMap<>(); for (int i = 0; i < n; i++) { map.put(new Object(), "v" + i); // key 没有留任何其它强引用 } System.gc(); System.gc(); Thread.sleep(500); // 接下来第一次调用 map.size() 会得到什么?

真实验证:GC 之后,size() 第一次调用就已经归零

10000 个 key,没有任何代码显式调用清理方法,直接看 GC 之后第一次和第二次 size() 调用的结果。

真实实测 · 10000 个无强引用的 key
插入 10000 个 key 后,gc 之前 map.size() = 10000
gc 之后,第一次调用 map.size() = 0
再调用一次 map.size() = 0

真实结果很干脆:GC 之后第一次调用 size() 就已经是 0,不需要额外调用任何方法先"通知" map 去清理。原因在 JDK 源码里:WeakHashMap.size() 方法内部第一步就会调用私有的 expungeStaleEntries(),把已经被 GC 加入 ReferenceQueue 的过期条目真实清理掉,再统计剩余数量——这个清理动作是惰性的(要等某次真实访问 map 才会触发),但一旦触发就是完整、及时的,不存在"先看到一半清理结果"的中间状态。

真实验证:保留部分强引用,精确验证不多不少

再做一组更严格的对照:10000 个里故意留 200 个强引用,GC 之后看 size() 是不是精确等于 200。

真实实测 · 10000 个 key,其中 200 个保留强引用
对照组插入 10000 个,其中 200 个保留强引用,gc 之前 map2.size() = 10000
gc 之后 map2.size() = 200(应该正好剩下保留强引用的那 200 个)
真实测出的结果和预期精确一致:200 个被强引用数组保留住的 key 全部真实留在 map 里,剩下 9800 个真实被清理干净
这和 HashMap 可变 key 那篇有什么关联?系列里HashMap 可变 key那篇真实验证过:对象状态变了会导致"找不到但没删除"的僵尸 entry。WeakHashMap 的自动清理某种程度上是相反的例子——它是 JDK 主动设计的、可预期的清理机制,不是意外的副作用,所以在需要"key 活多久,对应缓存就活多久"这种场景下(比如给某个对象挂一些附加元数据),WeakHashMap 是真实可靠的选择,前提是理解它靠的是访问触发清理,不是后台线程定时清理。

分步演示:一个entry的key被回收后,WeakHashMap真实发生了什么

每一行都是真实运行这段代码得到的结果。

WeakHashMap 真实靠key的弱可达性自动清理entry;换成普通HashMap,同样的操作真实完全不会触发任何清理
这一步新验证的map状态

    参考与说明

    • 本文全部 size() 结果均在本机真实运行 Java 17、两次 System.gc() + 500ms 等待后得到,两组数据(10000→0,10000→200)结果精确、可重复。
    • 弱引用被清理的真实时机问题,和finalize 复活那篇里 WeakReference 提前清空的细节是同一类底层机制。
    • 没有涉及:WeakHashMap 不适合用作通用缓存(没有大小限制、没有访问频率淘汰策略)的场景局限、Guava Cache/Caffeine 等专业缓存库的对比——这些留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号