3 ms·
Closures aren’t the only case where this happens. And your solution is not adequate: the closure does reference the variable, but it’s possible to have a refer
by pizlonator 2y ago
Closures aren’t the only case where this happens.
And your solution is not adequate: the closure does reference the variable, but it’s possible to have a reference to the closure that exists for some reason other than calling the closure (the second closure does this - it references the closure that references the variable, but won’t call the closure that references the variable). It’s not straightforward to solve that problem, and the solution involves a harder constraint system than just object-to-object reachability.
- paulddraper 2y agoThis is just tracing GC. Nothing more or less than that. The only difference is how you construct the closure record for your GC. Using the example: function f() { const a = {}; const b = {}; return [ () => a, () => b, ]; } You can choose to have one scope record shared by both functions, or individual record. function1 ----| ----> a v | scope ^ | function2 ----| ----> b Or function1 --> scope1 --> a function2 --> scope2 --> b JS engines choose to do the former, not out of simplicity (arguably it's more complicated), but due to performance.
- deredede 2y agoThe language specification says to use a single scope (see olliej's detailed comment). You could change it to use separate scopes here but it won't solve the more generic issue of imprecise captures, just this specific case.
- pizlonator 2y agoNo, JS engines choose it out of spec compliance. (Source: I worked on a JS engine for 10.5 years.) And your solution doesn’t solve the problem. The problem is you’ve got once closure that does close over a reference, and another closure that closes over the first closure, just not to call it, but because the function pointer carries meaning for the purpose of clearing the timeout. A JS engine could solve this by saying that the state closed over by a closure is only live if the closure itself is live for the purpose of a call, but defining that is next to impossible given JS’s dynamism (the runtime would have to prove that clearTimeout doesn’t call its argument, which is a crazy high proof burden in JS).
- paulddraper 2y agoI don't know how to square this with your credentials, but the spec does not specify the GC behavior of closures. (This only thing it's ever said about GC was WeakMap/WeakSet in ES2015.) I'm sorry, I literally do not understand what you are saying. Java does closures this way. Python does closures this way. Google has considered not doing closures that way in JS. [1] This is an intentional but extremely optional implementation choice, made for performance reasons. (See Chromium thread.) [1] https://issues.chromium.org/issues/41070945 https://issues.chromium.org/issues/41070945
- pizlonator 2y agoIf you want to understand what I’m saying, then consider carefully if your solution even fixes the leak. It doesn’t. The original post isn’t clear on this and maybe that’s what you’re confused about. You’re right that the spec doesn’t call for a particular closure representation, but it makes only one representation practical due to the shared mutable state of scopes, which is mandated by the spec. This makes JS’s closures unlike closures in other languages, which allow for greater flexibility. But thats off topic for the original post - the leak - since that leak would exist even with your proposed separate scopes. Again: stated most precisely, and as I’ve said more than once in the thread, the leak is that closure #2 closes over closure #1, not that closure #2 closes over a separate variable in the same scope. Making the scopes separate won’t fix the leak.
- Zacru 2y ago>closure #2 closes over closure #1 I don't see this in the example. Closure #1 is never bound in the scope, as far as I can tell. Could you explain how closure #1 is closed over without being bound?
- pwdisswordfishz 2y ago> (This only thing it's ever said about GC was WeakMap/WeakSet in ES2015.) Even later than that — only when WeakRef and FinalizationRegistry appeared in ES2021 did TC39 bother to specify any GC behavior — and badly. https://es.discourse.group/t/liveness-barriers-and-finalization/1082 https://es.discourse.group/t/liveness-barriers-and-finalizat...