简体   繁体   English

为什么 JavaScript 循环不会溢出堆栈?

[英]Why JavaScript loops do not overflow stack?

I've got a couple of questions about memory allocation in JavaScript我有几个关于 JavaScript 中的 memory 分配的问题

As far as I know, JavaScript primitives are immutable and stored in the stack.据我所知,JavaScript 原语是不可变的并存储在堆栈中。 If we change the value of a primitive or if we assign a new variable to the old variable, it creates a new memory location for each case.如果我们更改原语的值或将新变量分配给旧变量,它会为每个案例创建一个新的 memory 位置。

let x = 2 
let y = x
x = 3
console.log(y) // 2

  1. When we run a large loop just as the example bellow, 99999999 times 8bytes needed in stack to allocate space for i.当我们像下面的例子一样运行一个大循环时,堆栈中需要 99999999 乘以 8 个字节来为 i 分配空间。 So, why the stack is not overflown?那么,为什么栈没有溢出呢?
    for(let i = 0; i<99999999; i++){
       let x = i
    }
  1. If millions of objects are created simultaneously (in a real world app), is the stack enough to hold references to all the objects in the heap?如果同时创建数百万个对象(在现实世界的应用程序中),堆栈是否足以保存对堆中所有对象的引用?

the key point here is that the code includes a garbage collector, once a memory allocation has been narked as not required, the GC is allowed to reallocated for a different use这里的关键点是代码包含一个垃圾收集器,一旦 memory 分配因不需要而被隐藏,则允许重新分配 GC 以用于不同的用途

this is a little oversimplified but lets looks at what's happening at each stage这有点过于简单了,但让我们看看每个阶段发生了什么

let x = 3; //create a memory allocation in memory and alias x, store the value 3 in memory

let y = x; //create a memory allocation in memory and alias y, store the value located in memory under the alias x in it

x = 2; //save the value 2 in memory location x

so the stack never exceeds a single state that is scoped to 2 allocuted variables因此堆栈永远不会超过单个 state 范围为 2 个分配的变量

for your loop example对于您的循环示例

for(let i = 0; i<99999999; i++){ //
   let x = i
}
/*
create a memory allocation in memory and alias i, store the value 0 in it
create a code marker for the for loop
create a memory allocation in memory and alias x, store the value located in memory under the alias i in it
check if the value held in memory location i < 99999999

   yes 
        save the value i+1 in memory location i set execute point to the previous save marker
        mark memory location aliased x as not required

   no 
        unset execution marker
        mark memory location aliased x as not required
*/

here their may be some growth in the memory usage from the allocation of the x variable in the loop however GC can easily clean this up在这里,从循环中 x 变量的分配来看,memory 的使用可能会有所增长,但是 GC 可以轻松地清理它

now for the issue you haven't considered recursion现在解决你没有考虑递归的问题

function doSomething(i){
    let x = doSomething(i);
    return x;
}
doSomething(i)
/*
create a code marker for the for function doSomething
create a state in the stack for doSomething
create a memory allocation in memory and alias i, store the value passed to do something in it

set passed value to value in i 
set current execution location to marker for to doSomething

*** will never happen because recursive calls never end ***
pop the previous code marker from the stack
set the return value in popped state to value in x
mark state as not required 
continue execution from popped code marker
       
*/

for this one every time you call the function doSomething the stack has an state added to it this state is a memory slot that stores the passed in value and any variables created in scope added to it when doSomething is completed this state is marked as no required however until the code can return the system has to keep allocating more and more room on the stack for each calls state this is why recursion is much less safe than normal loops for this one every time you call the function doSomething the stack has an state added to it this state is a memory slot that stores the passed in value and any variables created in scope added to it when doSomething is completed this state is marked as no required然而,在代码可以返回之前,系统必须为每次调用 state 在堆栈上分配越来越多的空间,这就是为什么递归比普通循环安全得多

(V8 developer here.) (这里是 V8 开发人员。)

  1. When we run a large loop just as the example bellow, 99999999 times 8bytes needed in stack to allocate space for i.当我们像下面的例子一样运行一个大循环时,堆栈中需要 99999999 乘以 8 个字节来为 i 分配空间。 So, why the stack is not overflown?那么,为什么栈没有溢出呢?

The premise is incorrect.前提不正确。 There's only one stack slot for i . i只有一个堆栈槽。 Each iteration of the loop reuses it.循环的每次迭代都会重用它。 Whether this loop runs once, or twice, or 100 times, or 9999... times therefore doesn't change how much stack space it needs.因此,无论此循环运行一次、两次、100 次还是 9999... 次,都不会改变它需要多少堆栈空间。

  1. If millions of objects are created simultaneously (in a real world app), is the stack enough to hold references to all the objects in the heap?如果同时创建数百万个对象(在现实世界的应用程序中),堆栈是否足以保存对堆中所有对象的引用?

The usable stack size is a little less than a megabyte (this is determined by the operating system);可用堆栈大小略小于一兆字节(这由操作系统决定); on a 64-bit platform that's enough for around 400K pointers, which (very roughly) translates to a few hundred thousand local variables distributed across all functions that currently have an activation (there's no specific number because "it depends").在 64 位平台上,大约 400K 指针就足够了,这(非常粗略地)转换为分布在当前具有激活的所有函数中的几十万个局部变量(没有具体数字,因为“它取决于”)。 So it's certainly possible to have more objects on the heap than can be referred to directly from the stack, but this is not typically a limitation that code runs into: for example, you could have a local variable pointing at an array, and that array can in turn refer to thousands of other objects.因此,堆上的对象当然可能比直接从堆栈中引用的对象多,但这通常不是代码遇到的限制:例如,您可以有一个指向数组的局部变量,并且该数组可以反过来引用数以千计的其他对象。 That way, a single stack slot can keep many objects easily accessible.这样,单个堆栈槽可以使许多对象易于访问。

(Nitpick: millions of objects are never created "simultaneously", they're always allocated after each other, maybe in quick succession.) (Nitpick:数以百万计的对象永远不会“同时”创建,它们总是一个接一个地分配,可能是快速连续的。)

As commenters have already said, there is no stack involved in your example, or at least not how you imagine there is.正如评论者已经说过的那样,您的示例中没有涉及堆栈,或者至少不是您想象的那样。

If you want to break your stack, try this instead:如果你想打破你的堆栈,试试这个:

function Overflow(count){
    console.log('count:', count)
    return Overflow(count + 1)
}

Overflow(0)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM