当 WeakMap 本身在 v8 中被 GC-ed 时值会发生什么
What happens with values when WeakMap itself is GC-ed in v8
当键的生命周期短于 Map 本身的生命周期时,WeakMaps 很有用。但是,我可以想象当地图的生命和键的生命完全独立时的情况(即 WeakMap 本身可以在其键之一被垃圾收集之前):
var wm = new WeakMap();
var obj = {};
wm.set(obj, someHeavyData);
wm = null; // obj is still alive.
在上面的例子中 obj
还活着。但是,我们无法再达到 someHeavyData
,因为原始 WeakMap 已被垃圾回收。因此,someHeavyData
也应该被垃圾回收。尽管如此,我怀疑它不会被 GC 编辑并且会产生内存泄漏,因为(据我所知)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 而不是 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;
}
}
使用这个class我们可以写:
var secrets = new WeakMap2D();
var alice = {}, bob = {};
secrets.set(alice, bob, HugeSecretData);
alice = null;
这里的目的是,我们希望 HugeSecretData 在 bob
或 alice
被 GC 处理后立即被 GC 处理。看起来只有当第二个键(即bob
)被垃圾收集时才会被GC-ed。
再说一次:任何人,请告诉我我错了,并解释这些数据将如何以及何时被垃圾收集。
应该回答你的问题:
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)
填满您的记忆需要几秒钟。
当键的生命周期短于 Map 本身的生命周期时,WeakMaps 很有用。但是,我可以想象当地图的生命和键的生命完全独立时的情况(即 WeakMap 本身可以在其键之一被垃圾收集之前):
var wm = new WeakMap();
var obj = {};
wm.set(obj, someHeavyData);
wm = null; // obj is still alive.
在上面的例子中 obj
还活着。但是,我们无法再达到 someHeavyData
,因为原始 WeakMap 已被垃圾回收。因此,someHeavyData
也应该被垃圾回收。尽管如此,我怀疑它不会被 GC 编辑并且会产生内存泄漏,因为(据我所知)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 而不是 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;
}
}
使用这个class我们可以写:
var secrets = new WeakMap2D();
var alice = {}, bob = {};
secrets.set(alice, bob, HugeSecretData);
alice = null;
这里的目的是,我们希望 HugeSecretData 在 bob
或 alice
被 GC 处理后立即被 GC 处理。看起来只有当第二个键(即bob
)被垃圾收集时才会被GC-ed。
再说一次:任何人,请告诉我我错了,并解释这些数据将如何以及何时被垃圾收集。
应该回答你的问题:
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)
填满您的记忆需要几秒钟。