3 ms·
Thanks for the explanation. I think I understand why the leak happens with the problematic case. But why doesn't the last example before the problematic exampl
by lele04 2y ago
Thanks for the explanation. I think I understand why the leak happens with the problematic case. But why doesn't the last example before the problematic example leak? Doesn't the cleanup closure still retain the demo_env.
This is the example I mean:
function demo() {
const bigArrayBuffer = new ArrayBuffer(100_000_000);
const id = setTimeout(() => {
console.log('hello');
}, 1000);
return () => clearTimeout(id);
}
globalThis.cancelDemo = demo();
[Edit]
The blog post got updated with some links to other articles, which made it clear to me now. It looks like variables are only saved in the context/env if they are used in an inner function. Although one of the blog posts says that this is an implementation detail but at least it seems to work like this in FF/SM and V8.
- olliej 2y agoI didn't want to go into implementation details as they'd complicate matters :D What you're seeing here is the big difference between specification language and implementation - the major JS engines perform free variable analysis and only put variables that are actually captured into the activation object. This means in that in the example `bigArrayBuffer` is simply never stored on or referenced by any object and becomes unreachable the moment demo() returns. Engines can do this because it's essentially unobservable (though the addition of weak references technically undermines this claim, mercifully no one has yet decided that the existence of a closure that _could_ hold an object should be required to). Anyway, the big reason for performing free variable analysis is not to save memory/prevent space leaks as discussed in this article, but to make the function run faster. The first (and biggest) thing you win is that if nothing is captured in a function with closures you don't have to create the lexical environment object: function my_sort(arr) { ... <imagine there are some variables or something> ... return array.sort((a,b)=>{ if (a<b) return -1; return 1}) } There is a lot of code that uses non capturing closures like this, but without doing any free variable analysis the my_sort function would have to allocate a new lexical environment object on every call, even though nothing is actually captured. That environment object allocation was the bit that was expensive enough to warrant doing the free variable analysis when I implemented it in JSC - the time required to perform the free variable analysis is less than the time spent allocating and collecting unnecessary lexical environment objects. As a bonus once you have performed the capture analysis you can also use that information for an bunch of other low cost/complexity perf improvements as well.