I suspect the hardest part is getting garbage collection correct around lexical scope. The browsers struggled with this for years.
I suspect the hardest part is getting garbage collection correct around lexical scope. The browsers struggled with this for years.
Arguably, they still do.
function f() {
var some = [];
while(some.length < 1e6) {
some.push(some.length);
}
function g() { some; }
return function() {};
}
var a = [];
var interval = setInterval(function() {
var len = a.push(f());
if(len >= 500) {
clearInterval(interval);
}
}, 10);
uses a very large amount of memory in all browsers. function f() {
var some = [];
while(some.length < 1e6) {
some.push(some.length);
}
function g() { some; }
}
var a = [];
var interval = setInterval(function() {
var len = a.push(f());
if(len >= 500) {
clearInterval(interval);
}
}, 10);
uses very little.https://stackoverflow.com/questions/19798803/how-javascript-...
https://blog.meteor.com/an-interesting-kind-of-javascript-me...
Multiple browsers had bugs with garbage collection of a basic part of JavaScript for years? What was the problem? I wonder what made them specific to lexical scopes, because a lexical scope is either just more entries on the stack, which is scanned for local variables in the same way, or objects on the heap, which is scanned for fields in the same way.
There are still a few cases to worry about. For example, during function activation: if you allocate anything between the time you switched to a new lexical scope, but before saving the old one somewhere in the root set you are in trouble. This is easy to do, because you want to just save it in a local C variable until you have allocated the thing where you are ultimately going to save it.
https://github.com/jhallen/ivy-lang
In general gc bugs (like all allocation related bugs) are a nightmare because the effect is always delayed from the bug.
All pointers are tracked automatically (except for specially optimized classes), so that part isn't so bad, but it's still (too) easy to accidentally use a stale pointer value.
At the moment I can safely collect after every statement, but there is still work to be done before I can claim to support collecting after every expression (like I'd ideally be able to - so I can collect when I run out of heap).
function f(){
let a = {};
return function(){};
}
In all (IIRC) major implementations, the object allocated for `a` will be held on to after the function returns because it exists in the lexical scope of the function returned (even though it isn't—and never can be—reached, as static analysis can prove).Basically all the other bugs I'm aware of are cases of C++ code not treating things as roots when necessary, mostly in DOM code.
In truth there are myriad ways to very casually and accidentally create pools of references that can't be garbage collected. I'll leave finding these as an exercise for the reader to google.
here's an old article by Douglas Crockford about a particular memory leak in IE's garbage collector that persisted for a very long time:
It isn't, per-se, a bug in the JS VM, given the JS VM's GC is merely acting on what the external code tells it (especially given IE used a separate heap for DOM objects).
If no enclosing scope references or maybe references it, you can treat it differently to those that are referenced. You don't even need to do actual escape analysis to get 99% of the cases.
//global variable
let closure;
{ // block
let x = function a (b) {
let c = b + 5;
return function (d) {
return d + c;
}
}
closure = a(5);
}
The variable named closure is in the global scope. The functions are long destroyed after the block closes. When does variable c get garbage collected? It is still needed by the global closure variable. All of that mess should be garbage collected when closure is garbage collected, but it can continue to live on as a different variable when used in an object or array.Yesterday I finished an article (https://mras0.github.io/mjs/doc/gc/initial.html) on how I implemented the GC and some of the challenges, if you're curious.