我在Node.js v11.9.0中做了一个很大的Map
,它一直失败,并显示“致命错误:invalid table size Allocation failed - JavaScript heap out of memory”.我的map的键和值不应该接近Node的堆大小,所以我试着只做一个map并插入数字键和值:
var N = Math.pow(2, 26);
var map = new Map();
for (var i = 0; i < N; i++) {
map.set(i, i + 1);
if (i % 1e5 === 0) { console.log(i / 1e6); }
}
- 这个数字似乎可疑地接近2^24,因此将上面的日志记录替换为
if (i > 16777200) { console.log(i); }
,我看到程序在成功打印“16777215”后立即崩溃,这比2^24少一个。
问题:Node的Map
中是否有记录限制条目数接近2^24?是否有任何方法可以提高该限制?
(N.B.将Node作为node --max-old-space-size=4096
运行并不能防止崩溃,因为Node使用的RAM远远少于4 GB。
(N.B. 2.我不认为这是哈希冲突问题,因为在我的实际代码中,Map包含(短)字符串而不是数字。
(N.B. 3.在Firefox的JavaScript控制台中运行上述程序并不会杀死Firefox-Firefox一直在添加超过3000万的条目。然而,Chrome就像Node一样崩溃。所以这可能是V8的限制。)
5条答案
按热度按时间6l7fqoea1#
V8开发人员在这里。我可以确认2^24是
Map
中的最大条目数。这不是一个bug,它只是实现定义的限制。限值由以下各项确定:
Map
的FixedArray
后备存储的最大大小为1GB(与整个堆大小限制无关)FixedArray
的最大元素数为1GB / 8B = 2^30 / 2^3 = 2^27 ~= 1.34亿Map
每个条目需要3个元素(键、值、下一个存储桶链接),最大负载因子为50%(以避免多次存储桶冲突导致的速度减慢),其容量必须是2的幂。2^27 /(3 * 2)向下舍入到2的下一个幂是2^24,这是您观察到的限制。FWIW,凡事都有限度:除了最大堆大小之外,还有最大
String
长度、最大Array
长度、最大ArrayBuffer
长度、最大BigInt
大小、最大堆栈大小等。这些限制中的任何一个都可能是有争议的,有时提高它们是有意义的,但是限制仍然存在。我不知道要怎么样才能突破这个限制,比如说,我也不知道2的因素是否足以满足你们的期望。lvmkulzt2#
我写了BigMap和BigSet类,允许超出限制,当达到限制时,我只需创建新的Map(或Set)。API与内置的Map和Set完全相同。
2o7dmzc53#
有趣的是,如果您更改代码以创建两个
Map
对象并同时插入它们,它们都会在完全相同的点16.7崩溃:当在任何给定的Map中创建的条目超过224个时,这里会发生一些奇怪的事情,而不是全局地跨所有Map对象。
我想你发现了一个需要报告的V8错误。
fruv7luv4#
我在
48,408,186
元素后得到了这个:在Node.js 17中使用
node --max-old-space-size=8192 script.js
。一个普通的对象
{}
做得更好。zz2j4svz5#
小的解决方法是在达到限制时将其划分为较小的Map,从而能够存储和访问比内置Map更多的元素:
试试这个测试:
如果您有兴趣使用与内置插件完全相同的接口的NPM包,您可以尝试
large-map
和large-set
用于Set。