How variables are allocated memory in Javascript?

I would like to know how local variables are allocated memory in javascript. In C and C++ local variables are stored on stack. Is it the same in javascript? or everything is stored in heap?


It's actually a very interesting area of Javascript. Details in the spec, but: Javascript's way of handling local variables is quite different from the way C does it. When you call a function, amongst other things a "variable environment" for that call is created, which has something called a "binding object". (Call it the "variable object" for short; saying "the binding object of the variable environment" is just a tad bit long-winded!) The variable object has properties for the arguments to the function, all local variables declared in the function, and all functions declared within the function (along with a couple of other things). Unqualified references (eg, the foo in foo , not obj.foo ) within the function are first checked against the variable object to see if they match properties on it; if they do, those properties are used.

When a closure survives the function returning (which can happen for several reasons), the variable object for that function call is retained in memory by the reference from the closure. At first glance, that would suggest that the stack isn't used for local variables; in fact, modern JavaScript engines are quite smart, and may (if it's worthwhile) use the stack for locals that aren't actually used by the closure. (Naturally, the stack is still used for keeping track of return addresses and such.)

Here's an example:

function foo(a, b) {
    var c;

    c = a + b;

    function bar(d) {
        alert("d * c = " + (d * c));
    }

    return bar;
}

var b = foo(1, 2);
b(3); // alerts "d * c = 9"

When we call foo , a variable object gets created with these properties:

  • a and b — the arguments to the function
  • c — a local variable declared in the function
  • bar — a function declared within the function
  • (...and a couple of other things)
  • When foo executes the statement c = a + b; , it's referencing the c , a , and b properties on the variable object for that call to foo . When foo returns a reference to the bar function declared inside it, bar survives the call to foo returning. Since bar has a (hidden) reference to the variable object for that specific call to foo , the variable object survives (whereas in the normal case, it would have no outstanding references and so would be available for garbage collection).

    Later, when we call bar , a new variable object for that call is created with (amongst other things) a property called d — the argument to bar . Unqualified references within bar are first checked against the variable object for that call; so for instance, d resolves to the d property on the variable object for the call to bar . But an unqualified reference that doesn't match a property on its variable object is then checked against the next variable object in the "scope chain" for bar , which is the variable object for the call to foo . And since that has a property c , that's the property used within bar . Eg, in rough terms:

    +----------------------------+
    | `foo` call variable object |
    | -------------------------- |
    | a = 1                      |
    | b = 2                      |
    | c = 3                      |
    | bar = (function)           |
    +----------------------------+
                 ^
                 | chain
                 |
    +----------------------------+
    | `bar` call variable object |
    | -------------------------- |
    | d = 3                      |
    +----------------------------+

    Implementations are free to use whatever mechanism they want under the covers to make the above seem to happen. It's impossible to get direct access to the variable object for a function call, and the spec makes clear that it's perfectly fine if the variable object is just a concept, rather than a literal part of the implementation. A simple implementation may well just literally do what the spec says; a more complicated one may use a stack when there are no closures involved (for the speed benefit), or may always use a stack but then "tear off" the variable object needed for a closure when popping the stack. The only way to know in any specific case is to look at their code. :-)

    More about closures, the scope chain, etc. here:

  • Closures are not complicated
  • Poor misunderstood 'var'

  • Unfortunatelly the answer is: It depends.

    There was a big shift in recent javascript engines that started to optimize much better than they used to. The answer used to be: "Local variables are stored in heap-allocated stack frames for closures to work". It is not so simple anymore.

    There has been (or used to be like 20-30 years ago) research for Scheme implementations and closure optimization (JavaScript inherited pretty much Scheme closures, except for continuations that make it even trickier).

    I do not have the paper links ready, but if you do not have incredibly efficient garbage collector you need to use stack as well. The tricky part is then dealing with closures, which need to have variables heap-allocated. For that different strategies are used. The result is a hybrid where:

  • by inlining functions, you can reduce the number of heap-allocated frames being allocated/deallocated significantly
  • some variables can be safely put on stack, since it's time-span is limited (it is often connected to inlining the function calls as well)
  • in some cases you know that you might be creating closure, but you can wait until that happen and then allocate heap stack-frame for it and copy the current values from stack
  • there are optimizations connected to tail-calls, where you can heap-allocate earlier and then reuse the stack frame for the next function call, but that is not used in javascript engines as far as I know currently
  • this field is changing really fast in several competing engines, so the answer will probably still be "it depends"

    In addition, in new versions of the language we will be seeing features like let and const that actually make it easier for engines to optimize the allocation decisions. Especially immutability helps very much, since you can copy values freely off the stack (and make then part of the closure object for example) without resolving collisions of changing variables from different closures.

    链接地址: http://www.djcxy.com/p/82524.html

    上一篇: 内存分配堆栈和堆

    下一篇: 在Javascript中如何分配变量内存?