About closure, LexicalEnvironment and GC

tl;dr answer: “Only variables referenced from inner fns are heap allocated in V8. If you use eval then all vars assumed referenced.”. In your second example, o2 can be allocated on the stack and is thrown away after f1 exits.


I don’t think they can handle it. At least we know that some engines cannot, as this is known to be the cause of many memory leaks, as for example:

function outer(node) {
    node.onclick = function inner() { 
        // some code not referencing "node"
    };
}

where inner closes over node, forming a circular reference inner -> outer's VariableContext -> node -> inner, which will never be freed in for instance IE6, even if the DOM node is removed from the document. Some browsers handle this just fine though: circular references themselves are not a problem, it’s the GC implementation in IE6 that is the problem. But now I digress from the subject.

A common way to break the circular reference is to null out all unnecessary variables at the end of outer. I.e., set node = null. The question is then whether modern javascript engines can do this for you, can they somehow infer that a variable is not used within inner?

I think the answer is no, but I can be proven wrong. The reason is that the following code executes just fine:

function get_inner_function() {
    var x = "very big object";
    var y = "another big object";
    return function inner(varName) {
        alert(eval(varName));
    };
}

func = get_inner_function();

func("x");
func("y");

See for yourself using this jsfiddle example. There are no references to either x or y inside inner, but they are still accessible using eval. (Amazingly, if you alias eval to something else, say myeval, and call myeval, you DO NOT get a new execution context – this is even in the specification, see sections 10.4.2 and 15.1.2.1.1 in ECMA-262.)


Edit: As per your comment, it appears that some modern engines actually do some smart tricks, so I tried to dig a little more. I came across this forum thread discussing the issue, and in particular, a link to a tweet about how variables are allocated in V8. It also specifically touches on the eval problem. It seems that it has to parse the code in all inner functions. and see what variables are referenced, or if eval is used, and then determine whether each variable should be allocated on the heap or on the stack. Pretty neat. Here is another blog that contains a lot of details on the ECMAScript implementation.

This has the implication that even if an inner function never “escapes” the call, it can still force variables to be allocated on the heap. E.g.:

function init(node) {

    var someLargeVariable = "...";

    function drawSomeWidget(x, y) {
        library.draw(x, y, someLargeVariable);
    }

    drawSomeWidget(1, 1);
    drawSomeWidget(101, 1);

    return function () {
        alert("hi!");
    };
}

Now, as init has finished its call, someLargeVariable is no longer referenced and should be eligible for deletion, but I suspect that it is not, unless the inner function drawSomeWidget has been optimized away (inlined?). If so, this could probably occur pretty frequently when using self-executing functions to mimick classes with private / public methods.


Answer to Raynos comment below. I tried the above scenario (slightly modified) in the debugger, and the results are as I predict, at least in Chrome:

Screenshot of Chrome debugger
When the inner function is being executed, someLargeVariable is still in scope.

If I comment out the reference to someLargeVariable in the inner drawSomeWidget method, then you get a different result:

Screenshot of Chrome debugger 2
Now someLargeVariable is not in scope, because it could be allocated on the stack.

Leave a Comment