(() => {
var arr = []; // Memory leak
for (let i = 0; i < 10000000; i++) {
arr.push(Math.random());
}
function emptyFn() {}
function invoke(options) {
window.__options = options;
}
(() => {
emptyFn(arr)
})()
invoke({
success: () => {},
fail: () => {},
});
})();
I run this code in Chrome, See the memory useage in devTools, the var arr never been GC
Two things come together here that seem to overwhelm the garbage collector's ability to detect that arr
can be collected. This simpler example demonstrates it. (Tested in Chrome 135.0.7049.42.)
(() => {
var arr = []; // Memory leak
for (let i = 0; i < 10000000; i++) {
arr.push(Math.random());
}
window.__retainer = () => {}; /* remove this retainer line */
(() => arr)(); /* replace this line with an alternative */
/* arr; // alternative */
})();
First, arr
is in the closure of a function that remains accessible from outside after the code has been executed (as window.__options.success
in your example, as window.__retainer
in mine), even though this function does not access arr
. If this retainer is removed, arr
can be collected.
Second, arr
is accessed inside that closure during the execution of a function that is not accessible from outside (this function execution is
(() => {
emptyFn(arr)
})()
in your example, and is (() => arr)()
in mine). The garbage collector seems unable to recognize when arr
can be forgotten after this function execution. If the line containing the function execution is replaced with the simpler alternative, then arr
can be collected (despite the retainer).
In the absence of the retainer, the garbage collector is able to "forget" arr
after the function execution.