closure implementation question

639 views
Skip to first unread message

Brendan Miller

unread,
Jun 13, 2011, 7:22:13 PM6/13/11
to v8-users
In V8 if I have a closure such as in this example where I have a
function f that returns an anonymous closure:

function f() {
var x = 0;
var y = 1;
return function() {
return x + y;
};
}

In V8 does the returned closure keep two pointers (one to x, and one
to y), or does it keep a single pointer back to the execution context
of f? Or does it do something else altogether?

Also, can anyone give me pointers to what files in the V8 source code
I should take a look at to get an understanding of how this works?

Partially, I'm just curious, and partially I'm wondering about
performance implications. If f defined way more variables, is each
closure going to chew up more memory to keep track of all of those
pointers?

Also, from a GC perspective, I'm wondering in the case that something
like this happens:
function g() {
var x = 0
var y = 1;
return function() {
return x;
};
}

// lets day h is a global that sticks arround forever.
var h = g();

Will y never be garbage collected since its part of an execution
context pointed to by the closure held in h?

Thanks, and again, pointers back to the source code are appreciated.

Andy Wingo

unread,
Jun 14, 2011, 6:44:25 AM6/14/11
to v8-u...@googlegroups.com
On Mon, 2011-06-13 at 16:22 -0700, Brendan Miller wrote:
> In V8 if I have a closure such as in this example where I have a
> function f that returns an anonymous closure:
>
> function f() {
> var x = 0;
> var y = 1;
> return function() {
> return x + y;
> };
> }
>
> In V8 does the returned closure keep two pointers (one to x, and one
> to y), or does it keep a single pointer back to the execution context
> of f? Or does it do something else altogether?

V8 stores all variables referenced from nested functions (x and y in
your case) on the heap. That way an update to a closed-over variable
from one nested scope will be visible in another. (Note that this is
only strictly necessary for variables which are ever mutated; variables
which are never set may simply be copied.) So closing over a variable
does currently have a small cost, in that it must be accessed through a
double indirection: once to get the pointer to the context object, and
another to dereference the variable.

See scope.h and scope.cc for a fairly well-commented analysis code. At
runtime the values are stored in an array in a "context" object; see
contexts.{h,cc}.

Note that Crankshaft can at times access the value of variables stored
in heap slots directly (in registers, for example).

> Partially, I'm just curious, and partially I'm wondering about
> performance implications. If f defined way more variables, is each
> closure going to chew up more memory to keep track of all of those
> pointers?

The cost is only paid for variables referenced by nested functions. The
cost also appears to be only once per scope, and not per-function; an
odd decision, and one that really falls more on the side of "objects are
closures" than "closures are objects" ;-)

http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html

> Also, from a GC perspective, I'm wondering in the case that something
> like this happens:
> function g() {
> var x = 0
> var y = 1;
> return function() {
> return x;
> };
> }
>
> // lets day h is a global that sticks arround forever.
> var h = g();
>
> Will y never be garbage collected since its part of an execution
> context pointed to by the closure held in h?

Y was never allocated on the heap in this case.

Happy hacking,

Andy

Reply all
Reply to author
Forward
0 new messages