WeakMap 能避免缓存导致的内存泄漏,因其键为弱引用:只要对象无其他强引用,即使作为 WeakMap 的键也能被 GC 回收;而 Map 的强引用会阻止对象回收,造成内存泄漏。

WeakMap 能在缓存计算结果时避免内存泄漏,关键在于它对键(key)的弱引用特性:只要参数对象在外部没有其他强引用,即使它被用作 WeakMap 的键,也能被垃圾回收器正常回收。
为什么普通 Map 会导致内存泄漏?
如果用 Map 缓存函数结果,例如:
const cache = new Map();
function expensiveCalc(obj) {
if (cache.has(obj)) return cache.get(obj);
const result = /* ...耗时计算... */;
cache.set(obj, result); // obj 被强引用!
return result;
}
每次传入新对象(如 {x:1}),cache 都会持有对该对象的强引用,导致对象永远无法被 GC 回收——哪怕调用方早已丢弃该对象,缓存却把它“锁住”了。
WeakMap 如何解决这个问题?
WeakMap 只接受对象作为键,且对键是弱引用。只要该对象在别处不再被引用,即使它还在 WeakMap 中,GC 仍可回收它,WeakMap 内部会自动清理对应条目(不暴露给开发者,但行为上等效)。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
示例:
const cache = new WeakMap();
<p>function expensiveCalc(obj) {
if (cache.has(obj)) return cache.get(obj);
const result = /<em> ...计算... </em>/;
cache.set(obj, result); // ✅ obj 不会被此操作“留住”
return result;
}</p><p>// 使用
const input = { x: 1 };
expensiveCalc(input);
// 后续若不再持有 input 引用,它可被 GC 回收,cache 不阻止
使用 WeakMap 缓存的注意事项
- 键必须是对象:不能用字符串、数字、Symbol 或 null 作 key;若需支持原始值,需额外封装(如用包装对象 + 原始值映射),但会失去 WeakMap 的自动回收优势
-
无法遍历或清空:WeakMap 没有
keys()、values()、entries()或clear()方法,这是设计使然——避免阻止 GC -
缓存命中依赖同一对象引用:
{x:1}和{x:1}是两个不同对象,WeakMap 视为不同 key;适合缓存“同一实例多次调用”的场景(如 React 组件 props、DOM 元素、配置对象实例) - 适合生命周期明确的对象:比如缓存某个 DOM 元素的布局计算结果,当元素从 DOM 移除且无其他引用时,WeakMap 不会阻碍其回收
进阶:支持多参数的 WeakMap 缓存方案
WeakMap 只支持单个对象作 key,若函数接收多个对象参数(如 fn(a, b, c)),可将它们组合成一个唯一标识对象(注意:该对象本身也需能被回收):
const cache = new WeakMap();
<p>function expensiveCalc(a, b, c) {
// 创建轻量包装对象,仅用于做 key
const key = { a, b, c }; // 注意:这里 key 是新对象,a/b/c 仍是原引用
if (cache.has(key)) return cache.get(key);</p><p>const result = /<em> ... </em>/;
cache.set(key, result);
return result;
}
⚠️ 但这样 key 对象本身又成了新内存负担。更稳妥的方式是只选其中一个**最稳定、生命周期最长**的对象作为 key(如主数据对象),其余参数参与计算但不纳入 key,或改用 FinalizationRegistry 配合手动清理(较复杂,通常非必要)。

















