微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么迭代 Set 的值会分配和创建垃圾?

如何解决为什么迭代 Set 的值会分配和创建垃圾?

昨天我问了 this question 如何在不分配的情况下迭代 Map。一位 v8 开发者回复如下:

但是如果你只对处理值感兴趣,你可以通过只迭代值来避免它被创建: for (let v of map.values()) {...} 不分配任何短-生活的对象。迭代 map.keys() 也是如此。

我正在尝试在测试中复制此内容,但遇到了问题。这是我制作的一个演示,它用一个简单的 Set 重现了我遇到的问题。将以下代码粘贴到一个空 HTML 文件的脚本标签中:

let a = new Set([ 1,2,3,4,5 ]);
let b = [ 1,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 运行而偶尔出现帧峰值。

解决方法

为什么迭代 Set 的值会分配和创建垃圾?

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

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

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

为什么任务管理器显示内存随时间增加?

我不认为它会这样做。我已经完全按原样复制粘贴了您的代码片段,并让它静置了几分钟;该选项卡的 Chrome 任务管理器的“JavaScript 内存”列没有改变一千字节。 (这实际上只能证明这个测试并不是真正的压力测试:一旦 sum 超过 1<<30,它开始疯狂分配,但这不是因为设置或数组...)


疯狂猜测:也许您安装了一些扩展程序,将代码注入每个页面并导致分配?无论如何,无论它是什么,它都不是 for..of 上的 Set 循环。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。