Skip to content

Map 的键是引用类型时,如何防止内存泄漏?

参考答案:

Map 的键是引用类型(如对象、数组、函数)时,如果不小心管理,会导致内存泄漏,因为:

  • Map 对键是强引用,只要该键存在于 Map 中,它就不会被垃圾回收;
  • 即使代码中不再显式使用这个对象,只要它还在 Map 里,它的内存就无法释放。

一、问题举例

js
const map = new Map();

function cacheUser(user) {
  map.set(user, computeHeavyStuff(user));
}

// 某个 user 对象,即使业务中已经不再用它,但 map 中还持有它
// → user 永远不会被 GC 回收,内存常驻

这在需要缓存大量对象时尤为危险,比如响应缓存、页面数据缓存等场景中,可能在用户无感知的情况下持续增加内存占用。

二、解决方案:使用 WeakMap

1. 什么是 WeakMap

WeakMap 是专为解决上述问题设计的数据结构:

  • 它的键只能是对象(引用类型);
  • 对键是弱引用,不阻止垃圾回收;
  • 如果没有其他强引用指向某个键对象,它会被 GC 回收,WeakMap 也会自动清理对应的键值对。

2. 示例替换

js
const cache = new WeakMap();

function cacheUser(user) {
  if (!cache.has(user)) {
    cache.set(user, computeHeavyStuff(user));
  }
  return cache.get(user);
}

当外部不再引用 user,这个对象就会被回收,WeakMap 不会阻止 GC。

三、WeakMap 的使用限制

  • 无法遍历(没有 .keys().values().entries());
  • 不能清空(没有 .clear() 方法);
  • 无法观察删除(不会触发事件);
  • 键必须是对象,不能是原始值。

这些限制是设计上的权衡,为的是让它成为GC 友好的结构,不干扰垃圾回收算法

四、何时用 WeakMap 替代 Map

适用场景:

  • 键为对象;
  • 不需要枚举所有键;
  • 缓存、映射、私有字段存储等用途;
  • 期望自动释放内存的临时结构。

不适用:

  • 需要遍历所有键值;
  • 键为原始值;
  • 需要清空缓存的场景(可考虑封装手动过期逻辑的 Map 替代方案)。

题目要点:

  • Map 对引用类型键是强引用,可能导致内存泄漏;
  • 使用 WeakMap 替代 Map 能在对象无外部引用时自动释放;
  • WeakMap 不可遍历,有使用上的限制,适合做“缓存”或“映射”用途;
  • 内存泄漏的本质是“无效引用被意外保留”,选择合适的数据结构是避免此类问题的关键。