【问题标题】:JavaScript pre-allocated array Uncaught RangeError: Invalid Array LengthJavaScript 预分配数组 Uncaught RangeError: Invalid Array Length
【发布时间】:2021-12-02 11:55:47
【问题描述】:

我有一个小的代码循环抛出 Uncaught RangeError: Invalid Array Length

我能够在 Google Chrome 控制台中使用它来重现它

const COUNT = 100_000_000;
const xValues = new Array(COUNT);
const yValues = new Array(COUNT);
for (let i = 0; i < COUNT; i++) {
    xValues[i] = i;
    yValues[i] = Math.sin(i * 0.000001);
}
console.log(`count: ${yValues.length}`);

这是开发者控制台中的输出

据我所知,Javascript 中的最大数组大小是 2^32-1?这里应该有足够的内存来分配,并且索引 i 永远不会是负数,也不会超出数组的边界。

奇怪的是,如果我使用这段代码,没有崩溃

const COUNT = 100_000_000;
const xValues = new Array(COUNT);
const yValues = new Array(COUNT);
for (let i = 0; i < COUNT; i++) {
    xValues[i] = i;
    yValues[i] = i;
}
console.log(`count: ${yValues.length}`);

分配给 yValues[i] 的值永远不会超出范围 -1、+1,因此我也不能将此视为数字超出范围的问题。

有人对此有所了解吗?

编辑:更新

另一个不起作用的场景。计算随机游走。

    const count = 100_000_000;
    const xValues = new Array(COUNT);
    const yValues = new Array(COUNT);
    let prevYValue = 0;
    for (let i = 0; i < COUNT; i++) {
        const curYValue = Math.random() - .5;

        xValues[i] = i;
        yValues[i] = prevYValue + curYValue;

        prevYValue += curYValue;
    }

这个也扔!但是

    yValues[i] = i 

很好¯\_(ツ)_/¯

编辑:更新 2

现在可以确认这是特定于浏览器的,如果您在 Firefox 中运行相同的测试,它可以工作,但浏览器要求您等待。

怀疑异常Uncaught RangeError 是错误报告的超时?

【问题讨论】:

  • 有趣!当i 达到 5,592,406 FYI(至少在我的机器上)时,它会失败。
  • i === 5592406 时,Math.sin() 行上的 Chrome 和 Edge 都会中断。如果你从5592406 开始,它会在11184812(5592406 * 2) 上中断,(5592406 * 3) 也是如此......
  • yValues[5592406] = Math.sin(5592406 * 0.000001) 单独通过,所以 RangeError not 似乎 only 与索引数量相关,但也以某种方式与内容相关存储在数组中? MDN 没有明确表示这种行为恕我直言。
  • 与计算无关。你只需要一个数组就可以得到相同的结果,并为其分配一个浮点数,例如0.5。行为上的差异显然与用于数组元素的内部数据类型有关:32 位整数或浮点数。请参阅分配 1e9(是 32 位整数)或 1e10(不是 32 位整数)的区别。与调用Math.randomMath.sin无关。
  • 完成,报告为可能的 Chromium 错误 bugs.chromium.org/p/chromium/issues/detail?id=1275993

标签: javascript arrays


【解决方案1】:

真正的原因在于 V8 内存优化。当您存储整数时 - 它存储 32 位数字,但是当您存储双数时 - 它以不同的方式存储(作为对象) - 所以 yValues 数组包含引用但实际值存储在堆中。因此,在您的示例中,您只使用了所有堆内存。要查看限制,请使用:console.memory,您会看到如下内容:

MemoryInfo {
totalJSHeapSize: 10000000, 
usedJSHeapSize: 10000000, 
jsHeapSizeLimit: 3760000000}

在我的浏览器中是3_760_000_000

堆上的对象占用 50+ 字节,所以我的限制大约是 69_000_000 个浮点数。

【讨论】:

  • 这很有趣,我们发现 2^26-1 是索引的限制,因此大约 67108863 或更低可以工作。奇怪的是一个双精度数组存储为每个值 50 个字节?!
  • 它实际上在内存中的不同位置每个值大约需要 80 个字节 :-) 但有时优化 & 需要更少。
  • 奇怪的是 - 如果值是 i32 格式,内存会更少吗?这是非常有趣的东西,如果人们读到这里,值得 +upvotes ^_^
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-24
  • 2021-08-10
  • 1970-01-01
  • 2010-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多