为什么遍历 Set 的值会分配和创建垃圾?



昨天我问了一个关于如何在不分配的情况下迭代Map的问题。一个v8开发人员回复如下:

但是,如果您只对处理值感兴趣,您可以通过只对值进行迭代来避免创建它:for(let v of map.values((({…}不分配任何短期对象。对map.keys((.进行迭代也是如此

我正试图在测试中复制它,但遇到了麻烦。这是我制作的一个演示,它再现了我使用简单Set时遇到的问题。将以下代码粘贴到脚本标记中的空HTML文件中:

let a = new Set([ 1, 2, 3, 4, 5 ]);
let b = [ 1, 2, 3, 4, 5 ];
let sum = 0;
function setIterate() {
for (let item of a.values()) {
sum += item;
}
}
function arrayIterate() {
for (let i = 0; i < b.length; i++) {
sum += b[i];
}
}
setInterval(setIterate, 1);
// setInterval(arrayIterate, 1);

如果你打开这个HTML文件,然后点击Shift+Escape,它应该会打开Chrome任务管理器。如果您查看Javascript Memory列,您将看到内存随着时间的推移而缓慢增长。

但是,如果对setInterval(setIterate, 1);行进行注释,取消对setInterval(arrayIterate, 1);行的注释并刷新页面,则会看到内存使用率保持不变。

也就是说,随着时间的推移,对Set进行迭代会产生垃圾堆积,而对数组进行迭代则不会。

有没有任何方法可以迭代一个Set,而不会随着时间的推移产生这种垃圾堆积?我正在开发一个浏览器游戏,并在渲染循环中迭代许多SetsMaps,由于GC的运行,偶尔会出现帧峰值。

为什么迭代一个集合的值会分配和创建垃圾?

没有。使用--trace-gc(在d8node中(对setIterate进行一百万次调用显示零活动如果函数每次迭代只分配一个字节(它不能;最小分配粒度是两个指针,即8个字节(,那么年轻一代在那段时间内至少会被填满一次。

有没有任何方法可以迭代Set而不会随着时间的推移产生这种垃圾堆积?

绝对;例如你是如何做的。

为什么任务管理器显示内存会随着时间的推移而增加?

我不认为它会这么做。我已经按原样复制粘贴了你的片段,并让它静置几分钟;";JavaScript存储器";Chrome任务管理器中用于该选项卡的列并没有丝毫变化。(这实际上只是证明了这个测试并不是一个真正的压力测试:一旦sum超过1<<30,它就会开始疯狂地分配,但这不是因为集合或数组…(


胡乱猜测:也许您安装了一些扩展,可以将代码注入每个页面并进行分配?无论如何,不管是什么,都不是for..ofSet上的循环。

最新更新