当 key 的生命周期短于 Map 本身的生命周期时,WeakMaps 很有用。但是,我可以想象映射的生命周期和键的生命周期完全独立时的情况(即 WeakMap 本身可以在其键之一之前被垃圾收集):

var wm = new WeakMap();
var obj = {};
wm.set(obj, someHeavyData);
wm = null; // obj is still alive.

在上面的例子中,obj 仍然存在。但是,我们无法再访问 someHeavyData,因为原始 WeakMap 是垃圾收集的。因此,someHeavyData 也应该被垃圾收集。尽管如此,我怀疑它不会被 GC-ed 并且会产生内存泄漏,因为(据我所知)v8 中的 WeakMaps 以某种方式(大致)实现如下:
class WeakMap {

    constructor() {
       this.symbol = Symbol();
    }

    get(key) {
       return key[this.symbol];
    }

    set(key, value) {
       key[this.symbol] = value;
       return this;
    }
}

这意味着如果任何数据曾经存储在某个弱映射中的键下,它将作为强引用存储到键中(这就是为什么键应该是 WeakMaps 中的对象)并且不会被垃圾收集,直到键本身是垃圾收集。

谁能告诉我我错了,它会被垃圾收集吗?

当我事先知道 map 将在其键之前被垃圾收集时,可以告诉我选择 Map 而不是 WeakMap。不幸的是,有很多合法的情况是事先不知道的。以二维 WeakMap 为例:
class WeakMap2D {

   constructor () {
      this.wm1 = new WeakMap();
   }

   get(key1, key2) {
      var vm2 = this.vm1.get(key1);
      return vm2 && vm2.get(key2);
   }

   set(key1, key2, value) {
      var vm2 = this.vm1.get(key1);
      if (!vm2) {
         vm2 = new WeakMap();
         this.vm1.set(key1, vm2);
      }
      vm2.set(key2, value);
      return this;
   }

}

使用这个类我们可以写:
var secrets = new WeakMap2D();
var alice = {}, bob = {};
secrets.set(alice, bob, HugeSecretData);
alice = null;

在这里,我们希望只要 bobalice 被 GC 处理,就应该对 HugeSecretData 进行 GC 处理。看起来只有当第二个键(即 bob )被垃圾收集时才会被 GC 处理。

再一次:任何人,请告诉我我错了,并解释这些数据将如何以及何时被垃圾收集。

最佳答案

应该回答你的问题:

var obj = {}
var weakmap = null;

while(true) {
  weakmap = new WeakMap()
  weakmap.set(obj, new Uint32Array(2048))
}

如您所见,obj 永远不会被释放,但 weakmap 已更改为新的 weakmap,然后旧的应该被释放。如果我在 NodeJS 中运行它,它将释放内存并且这个无限循环最终不会占用所有内存。也就是说,WeakMap 按预期工作,没有内存泄漏。您认为 key 保留对 value 的引用是错误的,因为在这种情况下, WeakMap 不会是真正的 WeakMap

Uint32Array(2048) 填满你的内存需要几秒钟。

关于javascript - 当 WeakMap 本身在 v8 中被 GC 处理时,值会发生什么,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35102399/

10-13 09:38