I initially logged this as an issue in the original project, which was moved right away into help topics, without a good explanation, so now I'm trying to ask here.
If we run the following code in NodeJS (v14, v16, v17):
let arr = new Array(1e8);
for(const a of arr) {
}
console.log('releasing...');
arr = null;
setTimeout(() => {
}, 1e6); // let the process hang idle
The process memory is released instantly by GC, and so it sits at ~ 10MB.
Now, if we just swap to any typed array, say Uint8Array:
let arr = new Uint8Array(1e8);
for(const a of arr) {
}
console.log('releasing...');
arr = null;
setTimeout(() => {
}, 1e6); // let the process hang idle
Now the process memory sits at ~108MB permanently, it is never released.
In the original post, I had a reply about "not enough memory" being used. This didn't make any sense to me, because if I increase the array size to 1e9, the same occurs, except for the typed array the process now sits at 1GB, permanently. For a single NodeJS process, that is a lot.
Can anyone please explain, if what I'm looking at is a genuine bug or something I do not understand?
UPDATE
My tests indicate that it may have something to do with the specific of the for-of iterator for typed arrays, because if I remove for-of iterator, and instead use the following:
let t;
for(let i = 0;i < arr.length;i ++) {
t += arr[i];
}
then suddenly the memory is released correctly. I wonder now if typed arrays have a bug inside their iterator implementation, one that leaks or freezes memory.
Tested on Windows 10, with NodeJS v14, v16 and v17
The garbage collector is working. Node reuses or frees the memory at some point, just not at a defined point.
Here is a test application that allocates an array, holds on to a second, releases the variable, then repeats. The allocated memory of the application does what we expect: it fluctuates up and down. If the garbage collector wasn't working it would just go up.
function allocatedAndDeallocateArray(callback) {
let arr = new Uint8Array(1e8);
setTimeout(
() => {
for(const a of arr) {
}
arr = null;
callback();
},
1000
);
}
let count = 0;
function performAllocation() {
if(++count < 180) {
console.log(`${count}: Allocating new array!`);
allocatedAndDeallocateArray(performAllocation);
}
}
performAllocation();