¿Por qué Node.js tiene uso de memoria incremental?

Tengo un archivo gameserver.js que tiene más de 100 KB de tamaño. Y seguí revisando mi administrador de tareas después de cada actualización en mi navegador y seguí viendo que el uso de la memoria de mi node.exe sigue aumentando con cada actualización. Estoy usando el módulo ws aquí: https://github.com/websockets/ws y pensé, sabes qué, lo más probable es que haya alguna pérdida de memoria en mi código en algún lugar …

Entonces, para verificar y aislar el problema, creé un archivo test.js y puse el bloque de código ws predeterminado:

 var WebSocketServer = require('ws').Server , wss = new WebSocketServer({ port: 9300 }); wss.on('connection', function connection(ws) { ws.on('message', function incoming(message) { console.log('received: %s', message); }); }); 

Y se puso en marcha:

Introduzca la descripción de la imagen aquí

Ahora, node.exe el uso de memoria de node.exe :

Introduzca la descripción de la imagen aquí

La parte incremental que me confunde es:

Si actualizo mi navegador que hace la conexión a este servidor websocket del puerto 9300 y luego miro hacia atrás en mi administrador de tareas … muestra:

Introduzca la descripción de la imagen aquí

Que ahora está en: 14,500 K

Y sigue aumentando en cada actualización, por lo que teóricamente, si sigo refrescándome, irá por las nubes. ¿Está previsto esto? ¿Hay una pérdida de memoria en el módulo ws algún lugar tal vez? Todo el motivo por el que pregunto es porque pensé que quizás en unos minutos o cuando el usuario cierre el navegador, volverá a aparecer, pero no lo hace.

Y la razón principal por la que quería hacer esta prueba porque pensé que tenía un problema de pérdida de memoria en mi código personal en algún lugar y solo quería comprobar si no era yo, o viceversa. Ahora estoy perplejo.

Ver una huella de memoria aumentada por una aplicación Node.js es un comportamiento completamente normal. Node.js analiza constantemente su código en ejecución, genera código optimizado, vuelve a código no optimizado (si es necesario), etc. Todo esto requiere bastante memoria incluso para las aplicaciones más simples (Node.js en sí está escrito en gran parte en JavaScript que sigue las mismas optimizaciones / desoptimizaciones que su propio código).

Además, a un proceso se le puede otorgar más memoria cuando la necesita, pero muchos sistemas operativos eliminan esa memoria asignada del proceso solo cuando deciden que es necesaria en otro lugar (es decir, por otro proceso). Por lo tanto, una aplicación puede, en los picos, consumir 1 GB de RAM, luego se activa la recolección de basura, el uso se reduce a 500 MB, pero el proceso aún puede mantener el 1 GB.

Detección de presencia de memory leaks.

Para analizar correctamente el uso de la memoria y las memory leaks, debe usar process.memoryUsage() Node.js.

Debe configurar un intervalo que voltee este uso de memoria en un archivo, es decir, cada segundo, luego aplique algo de “estrés” en su aplicación durante varios segundos (es decir, para servidores web, emita varios miles de solicitudes). Luego observe los resultados y vea si la memoria sigue aumentando o si sigue un patrón constante de aumento / disminución.

Detección de fuente de memory leaks.

La mejor herramienta para esto es probablemente node-heapdump . Lo usas con el depurador de Chrome.

  1. Inicie su aplicación y aplique un esfuerzo inicial (esto es para generar un código optimizado y “calentar” su aplicación)
  2. Mientras la aplicación está inactiva, genera un heapdump
  3. Realice una sola operación adicional (es decir, una solicitud más) que sospeche que probablemente cause una pérdida de memoria; esta es probablemente la parte más difícil, especialmente para aplicaciones grandes
  4. Generar otro heapdump
  5. Cargue ambos heapdumps en el depurador de Chrome y compárelos: si hay una pérdida de memoria, verá que hay algunos objetos que se asignaron durante esa única solicitud pero que no se liberaron posteriormente.
  6. Inspeccione el objeto para determinar dónde se produce la fuga

Tuve la oportunidad de investigar una pérdida de memoria notificada en el marco de Sails.js: puede ver una descripción detallada del análisis (incluidos gráficos bonitos, etc.) sobre este problema .

También hay un artículo detallado sobre cómo trabajar con heapdumps por StrongLoop. Le sugiero que lo eche un vistazo.

El recolector de basura no se llama todo el tiempo porque bloquea su proceso. Entonces V8 lanza GC cuando cree que es necesario.

Para averiguar si tiene una pérdida de memoria, le propongo encender el GC manualmente después de cada solicitud para ver si su memoria aún está aumentando. Normalmente, si no tiene una pérdida de memoria, su memoria no debería boost. Porque el GC limpiará todos los objetos no usados. Si su memoria sigue aumentando después de una llamada del GC, tiene una pérdida de memoria.

Para lanzar GC manualmente puedes hacer eso, ¡pero atención! No uses esto en producción; esta es solo una forma de limpiar la memoria y ver si tiene una pérdida de memoria.

Inicia Node.js de esta manera:

 node --expose-gc --always-compact test.js 

Expondrá al recolector de basura y lo forzará a ser agresivo. Llame a este método para ejecutar el GC:

 global.gc(); 

Llame a este método después de cada golpe en su servidor y vea si el GC limpia la memoria o no.

También puede hacer dos acumulaciones de su proceso antes y después de la solicitud para ver la diferencia.

No uses esto en producción o en tu proyecto. Es solo una forma de ver si tiene una pérdida de memoria o no.