3 ms·
Fundamental downside of tracing garbage collection. Reachability is a conservative approximation of the set of objects that need to be kept alive. Not saying
by pizlonator 2y ago
Fundamental downside of tracing garbage collection.
Reachability is a conservative approximation of the set of objects that need to be kept alive.
Not saying don’t use GC, but this is a great example of how there are no silver bullets in memory management - only imperfect trade offs.
- paulddraper 2y agoNot really. It is trivially knowable that () => clearTimeout(id) does not reference bigArrayBuffer. The design is not because it is hard to know what needs to be kept alive, but rather it is more efficient to use one closure record with id and bigArrayBuffer than two different ones.
- pizlonator 2y agoFix this one issue and you’ll find another.
- paulddraper 2y agoIt's pretty simple: If the closure doesn't reference a variable, it doesn't need the variable. Can't get more basic than that.
- pizlonator 2y agoClosures 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
- kaba0 2y agoYou are not absent from conservative approximation even with manual memory management. E.g. a map is a trivial example where you can’t always know which elements will be used during the lifetime of the program (and only those has to be kept alive).
- pizlonator 2y agoYeah totally. No silver bullets.