【问题标题】:JavaScript heap out of memory when looping over map?循环地图时JavaScript堆内存不足?
【发布时间】:2019-04-10 04:56:12
【问题描述】:
const map = {}

for (let i=0;i<10**5;i++) {
    map[i] = true
}


let ans = 0

for (let i in map) {
    for (let j in map) {
        ans += i+j
    }
}


console.log(ans)

上述代码使用node运行时返回如下错误-

致命错误:在堆限制分配附近标记压缩无效 失败 - JavaScript 堆内存不足 1: 0x100037ddb node::Abort() [/usr/local/bin/node]

有人可以解释原因吗? ma​​p 被实例化得很好。只有当我遍历 ma​​p 键并将它们添加到我的 ans 变量时我才会遇到这个问题?

但是以下类似的代码可以正常工作并打印 ans -

let ans = 0

for (let i=0;i<10**5;i++) {
    for (let j=0;j<10**5;j++) {
        ans += i+j
    }
}

console.log(ans)

这背后的逻辑是什么。为什么循环遍历 map 中的键如此糟糕?

节点版本v10.7.0

【问题讨论】:

    标签: javascript node.js dictionary


    【解决方案1】:

    问题是您的键是字符串,而不是数字。添加前需要调用 parseInt() 或 Number() 进行转换:

    for (let i in map) {
        for (let j in map) {
            ans += Number(i) + Number(j)
        }
    }
    

    循环仍然会花费很长时间(您正在迭代 10**10 次),但您不会积累一个巨大的字符串来破坏内存使用。

    更新:屈服于使用 Number() 而不是 parseInt() 的首要地位。

    【讨论】:

    • 所以您是说错误是由于 OP 创建了一个“太大”的字符串? FWIW,不需要praseInt。使用Number(i)+i
    • 是的。字符串变得很大。是的,还有其他方法可以将字符串转换为数字。我不认为特定的优化是问题的重点。
    • “我不认为特定的优化是问题的重点。” 当然。我只会使用最简单的解决方案。 parseInt 是不必要的。真正的优化是移动i = parseInt 或外循环中的任何内容。
    • 如果您使用 parseInt,请使用基数 parseInt(string, 10) :)
    • 我会在答案中添加选项,但不确定为什么 parseInt 比 Number() 更有意义或更差。也许是另一个问题的话题。 :-)
    【解决方案2】:

    当使用for..in 时,您将遍历所有可枚举属性,包括原型链上的继承属性(对于对象,there are quite a few

    您需要使用hasOwnProperty 保护循环免受继承的道具的影响,因为它是outlined in the example on MDN

    【讨论】:

    • 这是真的,Object.prototype 有大约 20 个属性。与 OP 创建的属性数量相比,这微不足道。此外,所有这些都是不可枚举的,所以不是问题。
    • 问题是这些道具是用推断的 toString() 变成字符串并加起来导致内存泄漏的连接
    • 不,这不是问题所在。字符串连接是问题所在,是的,但同样,Object.prototype 中的任何属性都不包括在内。另外,属性 names 永远不是函数。证明:var ans = 0; for (var i in {1: true, 2: true}) ans += i; console.log(ans);
    【解决方案3】:

    接受的答案中提到的原因是我正在添加字符串。但是从 string 转换为 int 也是一项代价高昂的操作,特别是在循环如此大量的数字时,它会花费很长时间。

    所以对于正在阅读这个问题并且必须使用地图的其他人来说,可以使用 Javascript Map 而不是我上面示例中使用的 Object 因为 Map 可以支持任何类型的键(不仅仅是字符串)。所以代码将是 -

    const map = new Map()
    
    for (let i=0;i<10**5;i++) {
        map.set(i, true)
    }
    
    let ans = 0
    
    for (const i of map.keys()) {
        for (const j of map.keys()) {
            ans += i + j
        }
    }
    
    
    console.log(ans)
    

    【讨论】:

    • 在这种情况下为什么不简单地使用数组呢?
    猜你喜欢
    • 2018-02-10
    • 2019-09-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-21
    • 2023-01-07
    • 2017-02-27
    • 2017-10-10
    • 2019-09-10
    相关资源
    最近更新 更多