我的问题是这是否是一个nodejs垃圾收集器错误?或者这是意料之中的事?
在Windows上运行node v14.15.0。
在为你的
this question
在涉及WeakRef对象时,我发现了一个关于垃圾收集的奇怪的东西,它似乎是一个可能的bug。分配给在一个变量中声明的变量的对象
for
let
变量超出了
对于
循环。这里感兴趣的变量命名为
element
这是它的循环。只是循环最后一次迭代的对象没有得到GCed(即
要素
最后指向):
// fill all the arrays and the cache
// and put everything into the holding array too
for (let i = 0; i < numElements; i++) {
let arr = new Array(lenArrays);
arr.fill(i);
let element = { id: i, data: arr };
// temporarily hold onto each element by putting a
// full reference (not a weakRef) into an array
holding.push(element);
// add a weakRef to the Map
cache.set(i, new WeakRef(element));
}
holding
有了这个:
holding.length = 0;
所有的值
要素
从那个循环中应该可以得到GC。唯一提到他们的是via
WeakRef
对象(不阻止GC)。
事实上,如果我让nodejs有一些空闲时间,那么除了由
对于
循环确实是GCed。但是,最后一个不是。如果我加上
element = null
到最后
对于
循环,最后一个就可以拿到GCed了。所以,nodejs没有清除
要素
要素
因此,您可以在这里看到整个代码(您可以将其放到一个文件中,自己在nodejs中运行):
'use strict';
// to make memory usage output easier to read
function addCommas(str) {
var parts = (str + "").split("."),
main = parts[0],
len = main.length,
output = "",
i = len - 1;
while (i >= 0) {
output = main.charAt(i) + output;
if ((len - i) % 3 === 0 && i > 0) {
output = "," + output;
}
--i;
}
// put decimal part back
if (parts.length > 1) {
output += "." + parts[1];
}
return output;
}
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve, t, v);
});
}
function logUsage() {
let usage = process.memoryUsage();
console.log(`heapUsed: ${addCommas(usage.heapUsed)}`);
}
const numElements = 10000;
const lenArrays = 10000;
async function run() {
const cache = new Map();
const holding = [];
function checkItem(n) {
let item = cache.get(n).deref();
console.log(item);
}
// fill all the arrays and the cache
// and put everything into the holding array too
for (let i = 0; i < numElements; i++) {
let arr = new Array(lenArrays);
arr.fill(i);
let element = { id: i, data: arr };
// temporarily hold onto each element by putting a
// full reference (not a weakRef) into an array
holding.push(element);
// add a weakRef to the Map
cache.set(i, new WeakRef(element));
}
// should have a big Map holding lots of data
// all items should still be available
checkItem(numElements - 1);
logUsage();
await delay(5000);
logUsage();
// make whole holding array contents eligible for GC
holding.length = 0;
// pause for GC, then see if items are available
// and what memory usage is
await delay(5000);
checkItem(0);
checkItem(1);
checkItem(numElements - 1);
// count how many items are still in the Map
let cnt = 0;
for (const [index, item] of cache) {
if (item.deref()) {
++cnt;
console.log(`Index item ${index} still in cache`);
}
}
console.log(`There are ${cnt} items that haven't been GCed in the map`);
logUsage();
}
run();
当我运行这个时,我得到以下输出:
{
id: 9999,
data: [
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
... 9900 more items
]
}
heapUsed: 805,544,472
heapUsed: 805,582,072
undefined
undefined
{
id: 9999,
data: [
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999, 9999,
... 9900 more items
]
}
Index item 9999 still in cache
There are 1 items that haven't been GCed in the map
heapUsed: 3,490,168
两个
undefined
需要行。id:9999对象的第二个记录输出不应出现。也应该是
. 而且,不希望找到仍在缓存中的id:9999对象。它应该是合格的GC。
一种可能的理论是V8优化器正在
要素
对于
循环,以避免在循环中一次又一次地创建它,但在循环完成后又不能使它符合GC的条件—实际上是将它提升到更高的范围。
另一种理论是GC并不总是块作用域粒度。
有没有虫子?