Gestión de Perdida o Fuga de Memoria en Node JS – Parte 2 (Final)

6 minuto(s)

En este Post continuamos con la Parte anterior llamada Gestión de Perdida o Fuga de Memoria en Node JS – Parte 1 en donde vimos varios conceptos sobre Perdida de memoria, recolección de basura y otros detalles, asimismo empezamos con un proyecto en Node JS para analizar y corregir la perdida de memoria y en esta Parte 2 continuaremos con estas pruebas.

Partes

Antes de continuar con este Post te invito a leer el artículo Que es Java y otros Detalles y también te invito a escuchar el Podcast: “Que Hacer Cuando Estamos En Casa”:

Spotify:

Sound Cloud:

Bien ahora continuemos con el Post: Gestión de Perdida o Fuga de Memoria en Node JS – Parte 2 (Final).

Ahora voy a saturar mi servidor Node JS, esto servirá para obtener resultados de cuanta perdida o fuga de memoria se esta produciendo y mas adelante procederemos a corregirlo.

Saturando el servidor

En este punto las cosas se ponen interesantes, hay varios artículos en Internet que dicen como depurar fugas o perdidas de memoria en un servidor, primero haciéndole una fuerte solicitud varias veces con herramientas como Artillery y luego se depura utilizando el comando node –inspect. Pero hay un problema importante con este enfoque. Imagina que tienes un servidor API con cientos de APIs con cada API que toma varios parámetros que ejecutan diferentes rutas de código. Entonces en escenarios del mundo real donde no tienes idea de donde se encuentra la fuga o perdida de memoria, puedes saturar la memoria para depurar la fuga, pasar parámetros posibles varias veces. Para mí eso suena muy complicado a menos que tengas herramientas como Goreplay que permite grabar y reproducir tráfico real en su servidor de prueba facilitándote las cosas.

Para solucionar este problema, lo depuraremos en producción, es decir, permitiremos que nuestro servidor se sature en producción (ya que obtendrá todo tipo de solicitud de API) y una vez que descubramos que el uso de memoria aumenta, comenzaremos a depurarlo.

Paquete Heapdump

Un heap en términos muy simples es el lugar donde todo se arroja y permanece allí hasta que Garbage Collector elimina lo que se supone es basura. Un heapdump es una instantánea del almacenamiento dinámico actual. Contendrá todas las variables y asignaciones internas que están actualmente presentes en un heap.

Entonces si de alguna manera comparamos la acumulación de basura de un servidor nuevo frente a la acumulación de basura de un servidor saturado en mucho tiempo, deberíamos poder identificar los objetos que Garbage Collector no está recogiendo.

Para hacer un heapdump, utilizaremos el paquete heapdump que me permite tomar un Heapdump del servidor mediante programación, para instalar este paquete ejecutamos el siguiente comando:


Terminando la Parte 1 de este Post creamos un servidor en Node JS en un archivo llamado app.js, voy hacerle unos cambios al código y quedaría de la siguiente manera:


En el código anterior instancio el paquete heapdump y tan pronto se iniciar el servidor la aplicación genera un heapdump, asimismo tenemos un endpoint o ruta de API llamada /heapdump a esta ruta la llamaremos mas adelante usando CURL desde la consola de comandos, cuando nos demos cuenta de que nuestro consumo de memoria ha aumentado.

Identificando la Pérdida o Fuga de memoria

Bien nuestro servidor esta implementado, imaginemos que ha estado funcionando durante varios dias, esta siendo afectado por una serie de solicitudes (solo una en nuestro caso) y hemos visto que el consumo de memoria de nuestro servidor se ha disparado, si deseas puedes ver esto usando herramientas como Prometheus, Clinic o Express Status Monitor.

Realizaré 2 solicitudes, una se creara al iniciar el servidor de Node JS ejecutando el comando node app y ejecutará automáticamente la ruta /saturarservidor y a la otra ruta /heapdump voy a hacerle una solicitud mediante CURL en mi consola de comandos, con esto tomaré 2 heapdumps, estas 2 solicitudes o heapdumps contendrán todos los objetos que Garbage Collector no pudo recolectar. Las 2 rutas o endpoints /saturarservidor y /heapdump la especifiqué en mi archivo app.js anteriormente.

Bien iniciare el servidor de Node JS ejecutando el siguiente comando en mi consola de comandos, se ejecutará automáticamente la ruta /saturarservidor:


Luego de arrancar mi servidor de Node JS, se me ha creado automáticamente en el directorio principal de mi proyecto un archivo llamado heapDumpAtServerStart.heapsnapshot.

Ahora haré una solicitud GET mediante CURL a la ruta /heapdump, para esto debo mantener mi servidor de Node JS en línea, lo mejor es abrir una nueva consola de comandos para no detener el servidor de Node JS, en mi nueva consola de comandos ejecuto el siguiente comando:


Luego de hacer la solicitud mediante CURL, se me ha creado un archivo llamado heapDump-1586766258795.heapsnapshot en el directorio principal de mi proyecto.

Una vez que tengo los 2 archivos heapdumps (Servidor nuevo y de larga duración), puedo comenzar a comparar.

Abro el navegador Google Chrome y presiono la tecla F12 para abrir las Chrome Dev Tools, luego voy a la pestaña Memory y activo el check Heap snapshot, luego en la parte izquierda hago clic con el botón derecho del mouse y selecciono la opción Load y cargo los 2  archivos heapdumps.

Después de cargar los 2 archivos heapdumps cambia la perspective a Comparison y selecciona en la lista el heapdump heapDumpAtServerStart (Instantánea del servidor de larga duración)

Podemos ir a Constructor y ver todos los objetos que Garbage Collector no limpio, la mayoría de los objetos son referencias internas que los nodos usan, podemos ordenarlos por la columna Alloc. Size y verificar la mayoría de las asignaciones de memoria pesadas que tenemos. Luego desglosamos array y buscamos (object elements), podemos ver que el array leaked contiene una gran cantidad de objetos que Garbage Collector no recoge o limpia.

En la imagen anterior entonces podemos señalar al array leaked como la causa del alto consumo de memoria.

Arreglando la Fuga o perdida de Memoria

Ya que ahora sabemos que el array leaked esta causando el problema, podemos ver el código y depurarlo de manera fácil, el problema que esta ocurriendo es que el array leaks [] está fuera del alcance del ciclo de solicitud y por lo tanto, su referencia nunca se elimina, podemos solucionarlo de manera sencilla colocándolo dentro del alcance del ciclo de solicitud:


Ahora podemos repetir los pasos anteriores y comparar nuevamente las instantáneas y veremos que el problema de fuga o perdida de memoria se ha corregido.

Conclusión

Probablemente se produzcan pérdidas de memoria en Lenguajes de Programación como JavaScript que cuenta con un motor recolección de basura, es fácil arreglar una pérdida de memoria, aunque identificarlos puede ser un verdadero dolor de cabeza. En este Post has aprendido conceptos básicos sobre la administración de memoria y como lo hacen varios Lenguajes de Programación, asimismo intentamos depurar una perdida o fuga de memoria y luego la reparamos.

Nota(s)

  • Los códigos expuestos en este tutorial pueden quedar obsoletos, ser modificados o continuar, esto no depende de mi, si no de los Desarrolladores que dan soporte a Node JS, JavaScript o al paquete heapdump empleado en este tutorial.
  • No olvides que debemos usar la Tecnología para hacer cosas Buenas por el Mundo.

 

Síguenos en nuestras Redes Sociales para que no te pierdas nuestros próximos contenidos.