weakhashmap 的内存释放由访问驱动而非 gc 驱动:无访问时失效 entry 积压、value 持续强引用;首次访问(如 get/size)触发 expungestaleentries() 导致内存骤降,形成“滞后峰”。

面试中不需要画真实性能曲线图,但可以用逻辑推演+关键数据点模拟出“释放局限”的性能表现,让面试官一眼看懂 WeakHashMap 的被动清理本质。
关键性能拐点:访问触发 vs GC 触发
WeakHashMap 不是“一GC就清”,而是“一访问才清”。这个特性会直接反映在内存占用曲线上:
- 无访问场景:GC 后 WeakHashMap 内存占用几乎不变——Entry 数组仍持有已失效的 Entry(key 已被回收,但 Entry 还在桶里,value 仍强引用);
-
首次访问触发:调用
size()、get()或entrySet().iterator()时,立刻执行expungeStaleEntries(),内存骤降; - 高频访问下:每次操作都顺带清理,内存曲线平滑下降,看似“及时”;
- 低频/零访问下:Entry 积压 + value 长期强引用 → 内存持续虚高,直到某次偶然访问才“爆破式释放”。
用两组对比实验说明(可口述演示)
你可以说:“我做过两个小实验,内存走势完全不同”:
-
Test A(只写不读):循环创建 1000 个 WeakHashMap,每个 put 一个大 byte[][] 作为 key 和 value,每轮后
System.gc()。结果:很快 OOM —— 因为没任何 get/size 调用,expungeStaleEntries()从不执行,失效 Entry 堆满内存; -
Test B(边写边查):同样循环,但在每次插入后加一句
maps.get(0).size()。结果:内存稳定,无 OOM —— 每次 size() 都强制清理前序 map 中已回收的 key 对应条目。
值(value)不随 key 回收而释放,是隐藏瓶颈
很多人误以为“key 回收 = 条目消失 = value 释放”,其实不是:
- WeakHashMap 只弱引用 key,value 是强引用;
- 只有清理 Entry 时,才会断开对 value 的强引用;
- 所以若 key 已被 GC,但一直没触发清理,value 就成了“孤儿强引用”,持续占内存——这是典型的延迟泄漏点。
如何向面试官呈现这个局限?一句话总结
“WeakHashMap 的内存释放不是由 GC 驱动的,而是由 Map 自身操作驱动的;它的性能曲线在横轴(时间)上会出现明显‘滞后峰’——GC 完成后内存不降,直到第一次访问才陡降。这不是 bug,是设计取舍:用访问开销换内存安全,但要求使用者必须保障合理访问频次。”











