4 ms·
> It has the concept of explicitly capturing variables from the surrounding context inside closures, like PHP does, instead of keeping the entire context alive
by curveship 12y ago
> It has the concept of explicitly capturing variables from the surrounding context inside closures, like PHP does, instead of keeping the entire context alive forever like Ruby or JS.
Just as a point of fact, javascript -- at least the v8 implementation I'm most knowledgeable of -- doesn't "keep the entire context alive forever." Only variables used in the closure are allocated into the closure (i.e. on the heap), the others are allocated on the stack and disappear as soon as the function returns.
I don't use iTunes so can't read their book, but I wanted to ask: you say that ARC is still their GC strategy, correct? So reference cycles are still an issue? I'm surprised at this. I can see reference counting being a strategy for moving a non-GC'd language to GC (like Objective-C), but to start a new language with that constraint is surprising.
- Udo 12y ago> Only variables used in the closure are allocated into the closure I'm not sure that's true. Look at the following code: var x = 123; var f = function(xname) { eval('console.log('+xname+');'); } f('x'); It's a dynamically named variable. Clearly, f() has access to the entire context that surrounds it, not just the objects explicitly used in the function code. In this example, the compiler could not possibly have known I was going to access x. This means in Javascript, as well as in Ruby, when you hand a closure to my code, I can access the entire environment of that closure. Contrast that with Lua, for example, where the compiler does indeed check whether an outer variable is being used and then it imports that variable from the context only. PHP does it most explicitly, forcing the developer to declare what outer objects they want to have available within the function.
- Sheepshow 12y agoIn your example, X is still in scope when f('x') is called. It doesn't require closure to work.
- Udo 12y agoI think you misunderstand what I'm trying to say. The point is not that x should be out of scope (why would it be?) The original assertion by curveship was that the outer context is not kept alive for the function, and that f() only gets access to the variables it explicitly imports from the outer context. And I thought this might be wrong, so I cooked up the example. Again, this is not about scope. This is about the fact that the function itself keeps a reference to the entire context it was created in, as opposed to just the things it explicitly imports. In this, it appears, Javascript works exactly as Ruby, which again makes the entire outer context available through the binding facility. I'm sorry if that wasn't clear from my description.
- curveship 12y ago> In this example, the compiler could not possibly have known I was going to access x. Right, but it knew you were going to use eval, and to support that, it had to allocate all local variables in the closure. That's why you saw this behavior. The same would happen if you used a 'with' construct.
- Udo 12y ago> Right, but it knew you were going to use eval, and to support that, it had to allocate all local variables in the closure. Wow, so there is actually special handling in the engine for this? So it does static analysis whenever it can, but not in these two cases?
- curveship 12y agoYes, the V8 compiler bails out of several optimizations if your function uses eval. You can see this in the profiler: functions which V8 wasn't able to optimize will have an alert sign next to them, and if you click it, it'll tell you what the issue was.
- Udo 12y agoVery cool, thanks for clearing that up!
- anonymoushn 12y agoThis is a bit troublesome! function f() {var x = 99; return function(a,b) {return a(b)};} f()(eval, 'console.log(x);') ReferenceError: x is not defined function f() {var x = 99; return function(a,b) {return eval(b)};} f()(eval, 'console.log(x);') 99 undefined
- curveship 12y agoYep, this is as per spec: http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2 http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2 . "Indirect" calls to eval (i.e. assigning eval to another variable, like you did by passing it as a param) are evaluated in terms of the global environment. "Direct" calls, like in your second example, use the local environment.
- pjmlp 12y agoSwift reuses the Objective-C runtime, so it had to be compatible in terms of memory management.
- curveship 12y agoOK, thanks. I guess that makes sense. Now that the website is working, I see that they're aiming at fairly seamless interop between Swift and Objective-C, so I guess they need a similar memory strategy.
- kevingadd 12y agoThis is not accurate. SpiderMonkey and V8 still retain the entire outer scope if any of the variables are used. See here for an example: https://www.meteor.com/blog/2013/08/13/an-interesting-kind-of-javascript-memory-leak https://www.meteor.com/blog/2013/08/13/an-interesting-kind-o... This bug is still not fixed. There's an issue open for it on the V8 tracker, I believe. It seems to have not gotten fixed in either engine because it's a difficult problem that affects a small subset of JS applications.
- curveship 12y agoSo go ahead and run his test. Things have changed :). Memory builds up 1mb/second, then after a few seconds, you'll see it drop back to zero, as the GC runs. V8 has seen a lot of really nice optimizations to closures over the last year. My favorite is that closures are no longer considered megamorphic.
- kevingadd 12y agoOh wow, when did that land? I was getting hit by that leak with JSIL in the last couple months. Can you link to the commit?
- curveship 12y agoNot sure the date. I first noticed the change back in April, when I was profiling some code. Ask vegorov: http://mrale.ph/blog/2012/09/23/grokking-v8-closures-for-fun.html http://mrale.ph/blog/2012/09/23/grokking-v8-closures-for-fun... .