Inicialmente registré esto como un problema en el proyecto original , que se movió de inmediato a los temas de ayuda, sin una buena explicación, así que ahora estoy tratando de preguntar aquí.
Si ejecutamos el siguiente código en 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 idleLa memoria de proceso es liberada instantáneamente por GC, por lo que se encuentra en ~ 10 MB.
Ahora, si solo cambiamos a cualquier matriz tipeada, digamos Uint8Array :
let arr = new Uint8Array(1e8); for(const a of arr) { } console.log('releasing...'); arr = null; setTimeout(() => { }, 1e6); // let the process hang idleAhora la memoria de proceso se encuentra en ~108 MB de forma permanente, nunca se libera.
En la publicación original, recibí una respuesta sobre el uso de "memoria insuficiente". Esto no tenía ningún sentido para mí, porque si aumento el tamaño de la matriz a 1e9, ocurre lo mismo, excepto por la matriz escrita, el proceso ahora se encuentra en 1 GB, de forma permanente. Para un solo proceso de NodeJS, eso es mucho.
¿Alguien puede explicarme si lo que estoy viendo es un error genuino o algo que no entiendo?
ACTUALIZAR
Mis pruebas indican que puede tener algo que ver con lo específico del iterador for for-of para matrices tipadas, porque si elimino el iterador for-of y en su lugar uso lo siguiente:
let t; for(let i = 0;i < arr.length;i ++) { t += arr[i]; }luego, de repente, la memoria se libera correctamente. Ahora me pregunto si las matrices tipeadas tienen un error dentro de la implementación de su iterador, uno que pierde o congela la memoria.
Probado en Windows 10, con NodeJS v14, v16 y v17
El recolector de basura está funcionando. El nodo reutiliza o libera la memoria en algún punto, pero no en un punto definido.
Aquí hay una aplicación de prueba que asigna una matriz, retiene un segundo, libera la variable y luego repite. La memoria asignada de la aplicación hace lo que esperamos: fluctúa hacia arriba y hacia abajo. Si el recolector de basura no estuviera funcionando, simplemente subiría.
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();