The array is still referenced in the module functions closure so can't be garbage collected:
// Implicit closure created by nodejs
(function(exports, require, module, __filename, __dirname) {
let bigArray
setTimeout(function() {
bigArray = new Array(9999999)
}, 5000);
setInterval(function () {
// `bigArray` is still in scope here so cannot be garbage collected
// If you set bigArray to undefined it will get GC'd
const used = process.memoryUsage().heapUsed / 1024 / 1024;
console.log(`Usage: ${Math.round(used * 100) / 100} MB`);
}, 1000);
});
If you were to declare the variable only inside the setTimeout function scope it would be GC'd without having to remove the reference yourself, e.g.
// Implicit closure created by nodejs
(function(exports, require, module, __filename, __dirname) {
setTimeout(function() {
let bigArray = new Array(9999999)
}, 5000);
setInterval(function () {
// `bigArray` is not in scope here so it will be GC'd
const used = process.memoryUsage().heapUsed / 1024 / 1024;
console.log(`Usage: ${Math.round(used * 100) / 100} MB`);
}, 1000);
});
Additionally, be aware that V8 could be behaving lazily when it's not constrained for memory.
You can try running your script with --expose-gc and force gc:
node --expose-gc index.js
const forceGC = () => {
if (global.gc) {
global.gc();
} else {
console.warn('No GC hook! Start your program as `node --expose-gc file.js`.');
}
}
let bigArray
setTimeout(function() {
bigArray = new Array(9999999)
}, 5000)
setInterval(function () {
bigArray = undefined;
forceGC();
const used = process.memoryUsage().heapUsed / 1024 / 1024;
console.log(`Usage: ${Math.round(used * 100) / 100} MB`);
}, 1000)
Also, you can give node less memory to work with to test how it behaves when it's running low on memory:
node --max-old-space-size=100 index.js // Give node just 100MB (defaults to 2GB)