Desmitificando las fugas de memoria en JavaScript

Rate this content
Bookmark

Hoy en día es posible escribir aplicaciones complejas con solo un puñado de desarrolladores en un corto período de tiempo, confiando en Frameworks y herramientas. Pero, ¿qué sucede cuando la aplicación se bloquea con errores de falta de memoria? ¿Cómo es posible garantizar tiempos de respuesta rápidos? Estos problemas aún se consideran difíciles de resolver.

Sumergámonos en los entresijos de Node.js y aprendamos sobre los perfiles y el recolector de basura. Comprender cómo funciona el sistema en profundidad te permite escribir código más seguro, más rápido y con menos errores.

Asegurémonos siempre de brindar la mejor experiencia para todos. Tanto para ti como para tus clientes. Encontremos esa fuga de memoria y solucionémosla.

Aprendizajes

  • Los participantes comprenden el manejo de memoria de Node.js y sus limitaciones.
  • Los participantes saben cuándo perfilar su aplicación para identificar fugas de memoria y código lento.
  • Los participantes son capaces de encontrar y solucionar la mayoría de las fugas de memoria.

33 min
24 Jun, 2021

Video Summary and Transcription

La charla trata sobre desmitificar las fugas de memoria en JavaScript, abordando temas como la asignación de memoria, las fugas de memoria típicas y los problemas, el manejo de descriptores de archivos y escuchadores de eventos, herramientas y técnicas para identificar fugas de memoria, solucionar fugas de memoria y reiniciar aplicaciones, y la experiencia personal de Ruben con las fugas de memoria.

Available in English

1. Demystificando las fugas de memoria en JavaScript

Short description:

Hola a todos. Mi nombre es Ruden Britschwurter. Soy miembro del TC de NodeJS. Trabajo como arquitecto de software principal en MyConvolt, y estoy feliz de estar aquí hoy en la conferencia de Node. Hoy voy a hablar sobre cómo desmitificar las fugas de memoria. ¿Son realmente tan difíciles de resolver? ¿Y cómo podemos facilitar el proceso? Una fuga de memoria ocurre cuando un programa informático administra incorrectamente la memoria de acceso aleatorio de manera que la memoria que ya no se necesita no se libera. Y la línea azul claramente muestra una fuga de memoria porque con el tiempo se asigna más y más memoria sin liberarla. Y esto es malo. Entonces, ¿cómo manejamos la memoria en JavaScript en particular? ¿No tenemos que preocuparnos por eso, verdad? Todo esto se hace de forma transparente y perfecta. Y la memoria se libera automáticamente. ¿Por qué debería haber una fuga de memoria en primer lugar?

Mi nombre es Ruden Britschwurter. Soy miembro del TC de NodeJS. Trabajo como arquitecto de software principal en MyConvolt, y estoy feliz de estar aquí hoy en la conferencia de Node.

Y hoy voy a hablar sobre cómo desmitificar las fugas de memoria. A menudo se considera que las fugas de memoria son algo difícil y complicado de resolver. ¿Pero es eso realmente así? ¿Y cómo podemos facilitar el proceso?

Para responder a eso, primero me gustaría responder a la pregunta de qué es realmente una fuga de memoria. Y estoy consultando Wikipedia para responder a esa pregunta. Una fuga de memoria ocurre cuando un programa informático administra incorrectamente la memoria de acceso aleatorio. De manera que la memoria que ya no se necesita no se libera. Con el tiempo, acumulamos más y más memoria. Y en el peor de los casos, el programa puede llegar a bloquearse porque no hay más memoria para asignar. Y ese es realmente el peor escenario. O tal vez estés en un entorno en la nube y tengas que pagar mucho más dinero porque tienes un escalado automático activo y se asigna más y más memoria.

Aquí tenemos un gráfico que muestra claramente cómo se vería una fuga de memoria en comparación con el uso de memoria y el tiempo de ejecución del programa. Y la línea amarilla representa un programa perfecto en su mayoría. Comienza y se asigna algo de memoria, luego hay altibajos y esto es perfecto para cada programa en su mayoría. A veces los picos pueden ser más altos o bajar un poco, pero en promedio es una línea plana. Y la línea azul claramente muestra una fuga de memoria porque con el tiempo se asigna más y más memoria sin liberarla. Y esto es malo.

Entonces, ¿cómo manejamos la memoria en JavaScript en particular? ¿No tenemos que preocuparnos por eso, verdad? Todo esto se hace de forma transparente y perfecta. Y la memoria se libera automáticamente. ¿Por qué debería haber una fuga de memoria en primer lugar? La memoria se divide en una memoria de pila y una memoria de montón. La memoria de montón es una memoria asignada dinámicamente y la pila la realiza el sistema operativo en un caso típico. Cada hilo tiene algo de memoria de pila. Es un algoritmo de último en entrar, primero en salir, también llamado LIFO. Es muy simple, muy rápido y la memoria asignada en la pila se recupera automáticamente cuando la función sale. Cuando comparamos eso con la memoria de montón, hay muchas cosas sucediendo porque la memoria de pila normalmente solo contiene punteros a la función que se está ejecutando actualmente. Y si empujamos en la pila el puntero que se está ejecutando actualmente, llegamos al montón, que es la memoria asignada dinámicamente. Y aquí tenemos una buena descripción general de V8 que se hizo para el Times of India.

2. Asignación de memoria y recolección de basura

Short description:

Hay muchas cosas sucediendo. Se asignan JSObjects, tenemos código fuente de JavaScript, código optimizado, código de expresiones regulares, cadenas y mucho más. La memoria del montón, en particular en V8, se divide en tres áreas. Tenemos la generación joven, la generación intermedia y la generación antigua. El recolector de basura se encarga de liberar automáticamente la memoria que asignamos, pero a veces puede volverse inestable.

Hay muchas cosas sucediendo. Se asignan JSObjects, tenemos código fuente de JavaScript, código optimizado, código de expresiones regulares, cadenas y mucho más. Esta es la memoria principal de nuestro programa en este caso.

Y la memoria del montón, en particular en V8, se divide en tres áreas. Nuevamente, tenemos la generación joven. Entonces, tan pronto como asignamos una nueva variable, digamos que decimos que let pool es la cadena 'test', entonces se asignará la memoria 'test' y se colocará en la generación joven. Esta memoria es relativamente pequeña. En su mayoría, JavaScript tendrá variables intermedias que se utilizan para calcular el siguiente valor inmediatamente. Y no necesitas la variable tan pronto como calculas tu próximo valor. Todo esto se hace de forma síncrona. Por lo tanto, queremos desechar toda esa memoria que no necesitamos lo antes posible. Entonces, esta generación joven solo sobrevivirá a una ejecución de 'scavenge'. Esta es la primera ejecución en la que nuestro programa crea una memoria y trata de deshacerse de las variables que ya no se utilizan. Y si alguna variable sobrevive a esa ejecución, se empuja a la generación intermedia. Y si sobrevive a la segunda ejecución, entonces se empuja a la generación antigua. Esta es la parte más grande de la aplicación. Normalmente se utiliza para variables largas y utilizadas. Cosas que reutilizas en cada aplicación que pueden contener punteros a muchas cosas. Y utilizamos un algoritmo diferente para liberar la memoria en este caso. El algoritmo utilizado se llama MarkSweep. Comenzamos desde nuestro objeto raíz. El objeto raíz en un navegador sería el objeto window, y en Node.js es global. Mientras que en JavaScript moderno estaría en ambos, solo globalmente disponible. Y utilizamos un algoritmo similar a un algoritmo recursivo donde comenzamos con el objeto raíz y simplemente conectamos cada punto. Intentamos conectar cada nodo que esté de alguna manera conectado a su objeto raíz. Todos los demás nodos, variables o asignaciones se liberarán. Y esto debería ser ideal para liberar toda la memoria que no se utiliza.

Ya hablé de que como desarrolladores no tenemos que preocuparnos por la memoria que asignamos porque se liberará automáticamente. Lo que se hace en segundo plano es que se ejecuta un recolector de basura y ese recolector de basura tiene mucho que hacer y en algún momento incluso puede volverse inestable y no funcionar como anticipamos. Así que tenemos que analizar eso un poco más de cerca.

3. Problemas y fugas de memoria típicos

Short description:

Un tipo de dato incorrecto y ejecutar demasiadas cosas en paralelo pueden causar problemas de memoria. Los escuchadores de eventos y los closures también pueden contribuir a las fugas de memoria. Los descriptores de archivos, cuando no se cierran correctamente, también pueden causar problemas. Es crucial comprender los detalles de implementación de JavaScript.

¿Cuáles son las fugas de memoria o problemas típicos? Esto no está en un orden específico y no todos son fugas de memoria reales, por ejemplo, los primeros dos. Entonces, un tipo de dato incorrecto sigue siendo algo en lo que tenemos que investigar porque esto es algo muy típico que encuentro con frecuencia. A menudo, los programas utilizan tipos de memoria que no son eficientes para la tarea que se desea resolver. Y luego, cuando manejas un data grande como este, vas a asignar una gran cantidad de memoria, aunque no necesitarías eso, simplemente usando un tipo de data diferente.

Usar el tipo de data correcto también puede ayudarte a evitar que tu programa se bloquee en caso de que encuentres un data más grande en algún momento. Y el segundo es que también intentamos ejecutar muchas cosas en paralelo. Probablemente a todos nos encantan las promesas y el async/await, y usamos Promise.all para mejorar el rendimiento de la aplicación, porque normalmente, como con Node, las cosas se ejecutan en un solo hilo y aún queremos hacer varias cosas al mismo tiempo, principalmente para tener múltiples llamadas remotas a procedimientos. Pero cuando simplemente usas demasiadas de esas al mismo tiempo, también tenemos que asignar memoria para todas estas etapas. Y cuando aún no se han completado, también pueden causar una excepción en tu programa porque no queda más memoria. Así que esto también es importante tener en cuenta.

Ahora vamos a la primera fuga de memoria real, los escuchadores de eventos. Algo que mucha gente sabe es que en los navegadores, hace algún tiempo, era típico adjuntar escuchadores de eventos inmediatamente a un elemento DOM específico en una lista a la que te importaría que se desencadenara un evento. Y luego a veces tenemos que agregar muchos y muchos escuchadores de eventos a todos estos elementos. En su lugar, quieres tener un solo escuchador de eventos en un nodo superior, que luego escuche todos los eventos que provienen de todos estos nodos inferiores. Y luego no tienes que adjuntar tantos escuchadores porque los escuchadores de eventos también ocupan memoria. Y a veces agregamos un nuevo escuchador de eventos para cada evento entrante. Esto sería un error de programación, pero ocurre con frecuencia.

Y más difícil aún son los closures. Porque a veces los closures evitan que el recolector de basura sepa cuándo una variable ya no se utiliza. Y cuando podemos liberar esa memoria. Así que este es un caso complicado. Luego, los descriptores de archivos también pueden causar problemas. Porque cuando abres un archivo, hay una cantidad limitada de archivos que puedes abrir al mismo tiempo. Y cuando ejecutamos nuestro programa, normalmente funciona, aunque no cerremos el archivo después de abrirlo, eso también puede causar problemas. Y siempre debemos asegurarnos de cerrarlo tanto en caso de éxito como en caso de no éxito. Por lo tanto, siempre debes tener un enlace de archivo que, sin importar si abres el archivo con éxito o no. Porque a veces puede haber algo intermedio y aún lo abres. Pero hubo un error en otro lugar. Y aún tienes que cerrarlo. La parte más complicada es que en JavaScript, y cuando miramos en Chrome o cualquier navegador basado en Chromium y Node.js también se ejecuta con V8, entonces tenemos que conocer algunos detalles de implementación.

4. Lectura de datos en fragmentos

Short description:

Leer demasiados datos en un solo fragmento puede ser ineficiente y provocar problemas de asignación de memoria. En cambio, transmitir datos en fragmentos pequeños es más rápido y elimina la necesidad de una asignación excesiva de memoria.

Voy a profundizar en algunos de ellos en un momento. Aquí hay un ejemplo de lo malo que puede ser si leemos demasiados data en un solo fragmento también. Entonces, tenemos un servidor HTTP y leemos un archivo grande, el usuario solicita un archivo. Esto va a llevar tiempo y no va a ser eficiente y va a asignar mucha memoria porque primero tenemos que leer toda esa memoria en la aplicación y una vez que esté completamente cargada, comenzaremos a enviar esos data al usuario, no antes. Y cuando no hay un solo usuario sino muchos usuarios que solicitan la misma ruta al mismo tiempo, también podríamos tener un problema de memoria porque entonces simplemente la memoria total disponible ya no será suficiente y la aplicación se bloqueará. En cambio, solo debemos preocuparnos por transmitir los data en fragmentos pequeños. Será mucho más rápido y no tendremos que preocuparnos por la asignación de memoria más. Simplemente obtenemos la primera parte del archivo que queremos leer y no importa si tiene varios gigabytes de tamaño, aún podemos enviarlo de inmediato en, digamos, unos pocos kilobytes. Y tan pronto como termine, habrá terminado. La memoria en línea será muy plana. No tenemos que asignar más que los pequeños fragmentos que queremos leer.

5. Manejo de Descriptores de Archivos y Event Listeners

Short description:

Y con los descriptores de archivos, también nos preocupamos por manejar el error. Abrimos el archivo, queremos escribir algo en él, pero nunca cerramos ese archivo nuevamente. Los event listeners podrían verse así. Imaginemos que tenemos una base de datos basada en eventos y queremos escribir algo en ella. Las cadenas son, como mencioné, tenemos esta cadena muy larga, de un millón de caracteres, y solo nos importan los primeros 20 caracteres. Aún es difícil prevenirlo, pero deberíamos preocuparnos por ello. De lo contrario, la aplicación podría colapsar y esta es la peor situación. Y si sucede, bueno, tenemos que lidiar con esa situación y tratar con esas fugas de memoria puede ser muy problemático. Tenemos que saber cómo identificar los puntos que causan esa fuga de memoria. Y esta es la parte más importante para mí. Así que aquí es donde nos vamos a enfocar ahora, cómo detectar y solucionar esas fugas de memoria.

Y con los descriptores de archivos, también nos preocupamos por manejar el error. Abrimos el archivo, queremos escribir algo en él, pero nunca cerramos ese archivo nuevamente. Y si lo hacemos muchas veces, la aplicación también tendría un problema. Siempre debemos asegurarnos de cerrar ese descriptor de archivo. Y eso se hace en el blog de la última semana, como mencioné antes.

Los event listeners podrían verse así. Imaginemos que tenemos una database basada en eventos y queremos escribir algo en ella. Y tenemos un event listener en data. Entonces hay un servidor HTTP y quiere conectarse a una database y quiere escribir algo en ella. Escribimos en ella y luego habrá una respuesta en el evento data. Pero en este caso, cometimos un error porque en cada solicitud de usuario, agregamos un nuevo event listener a esa database en lugar de solo uno al principio. Y luego tenemos muchos event listeners al final que son todos muy pequeños, pero terminará como una línea azul que viste antes. Y esta es la peor, prácticamente. Es muy frecuente verlo reportado tanto en los informes de problemas de Node.js como en los de V8 porque esta es una parte específica de V8 que simplemente debes conocer.

Las cadenas son, como mencioné, tenemos esta cadena muy larga, de un millón de caracteres, y solo nos importan los primeros 20 caracteres. Entonces cortamos los 20 y normalmente podrías imaginar que el resto simplemente se leería porque ya no lo usamos. En cambio, solo tenemos una referencia porque internamente en V8 hay muchas optimizaciones en marcha y trata de ser muy rápido y eficiente en memoria al mismo tiempo. Es una heurística que se utiliza que puede que no siempre funcione como se anticipa. Imaginemos que tenemos la cadena 'test' y la cadena 'ABC' y quieres compararlas. En lugar de asignar un nuevo fragmento de memoria que consistiría en 'test A, B, C', simplemente asignaría algo nuevo y muy pequeño que apunta a la cadena 'test' y a la cadena 'ABC' y diría, soy la combinación de ambas. Es como una estructura de árbol que podrías imaginar. Cuando queremos cortar algo de nuevo, también simplemente apuntará a la cadena original y dirá, hey, tengo este punto de inicio y este punto final de esa cadena, pero no liberará el resto de la cadena aunque ya no se use. Y eso podría terminar en una situación difícil donde ensuciamos nuestro programa una y otra vez y como todos sabemos, no debemos ensuciar nuestro entorno. Aún es difícil prevenirlo, pero deberíamos preocuparnos por ello. De lo contrario, la aplicación podría colapsar y esta es la peor situación. Y si sucede, bueno, tenemos que lidiar con esa situación y tratar con esas fugas de memoria puede ser muy problemático. Incluso podríamos tener problemas de ira y pasar mucho tiempo investigando algo sin saber realmente qué buscar. Tenemos que saber cómo identificar los puntos que causan esa fuga de memoria. Y esta es la parte más importante para mí. Así que aquí es donde nos vamos a enfocar ahora, cómo detectar y solucionar esas fugas de memoria.

6. Herramientas y Técnicas para Identificar Fugas de Memoria

Short description:

Existen herramientas y banderas para identificar fugas de memoria en Node.js, como 'inspect' y 'trace GC'. La bandera 'inspect' te permite conectarte al inspector de Chrome u otras herramientas. El comando 'trace GC' muestra lo que hace el recolector de basura en diferentes momentos. La bandera 'abort on uncaught exception' provoca un volcado de memoria cuando la aplicación se bloquea. También se pueden utilizar herramientas como LM node. Para obtener más información, visita la guía de depuración de nodes.js.org. Ahora te mostraré algunos ejemplos de código, incluido un programa que confunde al recolector de basura. Podemos usar la bandera 'inspect' para ver el objetivo remoto y explorar capturas de memoria.

Existen muchas herramientas y también banderas para identificar fugas de memoria en Node.js. La primera es '--inspect', que se puede pasar al tiempo de ejecución de Node.js. Luego puedes conectarte, por ejemplo, al inspector de Chrome, pero también a otras herramientas. También tenemos una bandera llamada 'trace GC'. Con el comando 'trace GC', es posible ver lo que hace el recolector de basura en diferentes momentos. También es interesante saber sobre esto.

A veces es muy difícil identificar realmente el error. En ese caso, es posible que desees usar la bandera '--abort-on-uncaught-exception', porque esto automáticamente provocará un volcado de memoria. Un volcado de memoria es el estado de la memoria en ese punto de tiempo actual. Entonces, cuando la aplicación se bloquea debido a esa excepción, podemos ver cómo era la aplicación en términos de memoria en ese estado. También hay más herramientas como LM node para investigar esto. Si te interesa más herramientas, simplemente visita el sitio web nodes.js.org/guides/debugging/getting-started y encontrarás mucha información al respecto.

Y quiero mostrarte algo de código en realidad. Por ejemplo, para la cadena uno. En este caso, tengo un pequeño programa y estoy usando el módulo V8 de Node.js y tenemos una variable llamada 'count' que simplemente se inicializa en cero, y tenemos una variable 'pool'. Tenemos la función 'run', que hará algunas cosas que veremos en un momento. Tenemos este intervalo que se ejecuta cada milisegundo. La función 'run' se activa cada milisegundo y luego creamos capturas de memoria cada dos segundos para conocer el estado de la aplicación en ese punto de tiempo. Aquí tenemos esta función interna que hace referencia a la original, mientras que la original hace referencia a 'pool' y 'pool' se actualiza en cada ejecución. Así que aquí simplemente confundimos al recolector de basura debido a la forma en que todas estas variables están conectadas entre sí. Veamos qué sucede en este caso. Esto sería esto. Quiero revisar ese programa. Ahora abro 'drone inspect' y gracias a la bandera '--inspect', ahora puedo ver el objetivo remoto. Voy a inspeccionarlo. Y aquí ves esta vista general, selecciona el perfil de tubería, captura de memoria, muestreo de asignación, etc. Quiero ver algunas de estas capturas de memoria.

7. Análisis de la Asignación de Memoria

Short description:

Así que voy a cargar uno. El programa crea nuevos a lo largo del tiempo. Podemos comparar uno de estos montones y ver dónde se fue toda tu memoria. Por ejemplo, hay una gran diferencia en el delta de tamaño de 2.4 megabytes. Vemos que hay muchas cadenas. Ahora sabemos que tiene algo que ver con funk. Definitivamente sabemos que asignamos demasiadas de estas cadenas porque las puedes ver todas. Son muy grandes y no están libres. Tienes que investigar eso. También es posible usar esto en un navegador. Puedes iniciarlo.

Y como puedes ver, el programa crea nuevos a lo largo del tiempo. Así que vamos a abrir el primero. Y vamos a abrir el último. Y también voy a detener el programa. Ahora tenemos estos dos, porque aquí puedes investigar y hay muchas data sucediendo. Y no está muy claro qué buscar. Pero podemos compararlo simplemente haciendo clic en comparación. Y ahora podemos comparar uno de estos montones, no solo instantáneas con el otro. Y luego es mucho más fácil ver dónde se fue toda tu memoria. Por ejemplo, aquí podemos ver que hay una gran diferencia en el delta de tamaño de 2.4 megabytes. Así que claramente este es un punto en el que quieres investigar. Y cuando investigamos, vemos que hay muchas cadenas. Estas cadenas, nuevamente, cuando las miramos, están en esta variable, en esta cadena. Y la cadena está en un objeto específico en ese punto de memoria. Y esto está en el contexto original y del sistema. De acuerdo, el contexto está en funk. Ah, esto es algo que conocemos en nuestro programa. Así que sabemos que funk estuvo aquí. Este es nuestro funk. Y luego simplemente puedes identificar la causa real. Ahora sabemos que tiene algo que ver con funk. Aunque no lo veas de inmediato, definitivamente sabemos que asignamos demasiadas de estas cadenas porque las puedes ver todas aquí. Y son muy grandes. Y no están libres. Así que sabes que tienes que investigar eso.

El tiempo se me está acabando, así que tengo que apurarme un poco. Pero quiero decir que también es posible usar esto en un navegador. Porque esta tooling también está disponible allí. Y por ejemplo, puedes iniciarlo.

8. Solucionando Problemas de Fugas de Memoria y Herramientas

Short description:

Haz clic en Inspeccionar, ve a memoria y mira la descripción general. Otro programa, StringCrash, ejemplifica el problema. Pasos para solucionar la fuga de memoria: monitorear la memoria, perfilar con herramientas como Inspeccionar y tomar capturas de pantalla. Ten cuidado, las capturas de pantalla son costosas. Compara las capturas de pantalla para identificar la fuga y solucionarla. Hay numerosas herramientas disponibles para ayudar, pero recuerda que la memoria es costosa y puede ralentizar tu sistema.

Y al hacer clic en Inspeccionar, y luego ir a memoria. Y aquí está también la descripción general de eso. Quiero mostrarte un poco más de otro programa rápidamente. Por ejemplo, esto es StringCrash. Así que aquí, tengo ese problema del que había hablado con el V8. Podemos ver el código. Esto está haciendo exactamente lo que había mencionado antes. Y el programa se bloquea muy rápido porque no puede asignar más memoria.

Muy bien. Así que los pasos para solucionar la fuga de memoria. En primer lugar, por favor, monitorea tu memoria. Usa, por ejemplo, un APM o algo así. Y luego sabrás cómo se ve tu perfil de memoria. Perfílalo con herramientas como Inspeccionar. Y toma muchas capturas de pantalla. Pero ten en cuenta que son muy costosas. No querrás hacerlo en producción en la mayoría de los casos. Siempre intenta hacerlo en un entorno controlado que sea el aeropuerto de testing. Si tienes que hacerlo en un entorno de producción, entonces puedes hacerlo con una configuración muy estricta en un tiempo de espera, solo en un contenedor específico o algo así, para hacerlo muy poco frecuente. Y luego puedes comparar esas muchas capturas de pantalla. Esa es una de las formas más eficientes de identificar la fuga desde mi perspectiva. Y luego simplemente puedes identificarla y solucionar el problema tan pronto como sepas de qué se trata.

Hay muchas herramientas que pueden ayudarte. Puedes consultar en línea en el sitio web del que había hablado. La memoria es costosa, no solo en términos de costo cuando tienes que pagar dólares o cualquier otra moneda por ella, sino también en términos de performance, porque cuando asignas más y más memoria, tu sistema se ralentizará. Así que realmente quieres evitar eso. Las capturas de pantalla de los montones son costosas por una razón similar. Será como una aplicación congelada en ese punto del tiempo. No puede hacer nada más mientras recopila toda la memoria de la aplicación.

9. Solucionando Problemas de Fugas de Memoria y Reiniciando Aplicaciones

Short description:

Elige cuidadosamente tu tipo de datos y tu estructura de datos. Intenta solucionar el problema real en lugar de reiniciar la aplicación. Uno de mis días más productivos fue deshacerme de 1,000 líneas de código. El 71% es un problema común que la gente encuentra. Reiniciar la aplicación no es una solución duradera.

Elige cuidadosamente tu tipo de datos y tu estructura de datos, y solo reinicia tu aplicación como último recurso. Intenta solucionar el problema real en su lugar.

Así que muchas gracias por estar aquí. Quiero terminar con una de mis citas favoritas de Ken Thompson. Uno de mis días más productivos fue deshacerme de 1,000 líneas de código, y puedo relacionarme muy bien con eso. Que tengas un buen día.

Hola. Hola, Ruben. Es un placer verte aquí en el escenario conmigo. Es un honor tenerte aquí, por supuesto. ¿Qué opinas? ¿El 71% es el porcentaje que esperabas? Más o menos. Es realmente algo común que la gente encuentra. Y es algo que a menudo las personas simplemente intentan solucionar en lugar de profundizar en el problema y arreglarlo. He visto aplicaciones que se reinician al menos una vez al día porque tenían una fuga de memoria que no podían encontrar, o que intentaron simplemente reducir el uso de memoria en general o aumentar la memoria que podían usar, y cosas así. Y antes de tener que reiniciar la aplicación, eso es algo que obviamente no es agradable. Tienes que pagar mucho dinero por eso. Así que solucionar esos problemas es realmente importante. Sí, realmente lo es. Y sí, reiniciar no es realmente una solución duradera, por supuesto.

10. Manejo del Ejemplo de Recorte de Cadena

Short description:

Para evitar mantener la cadena original en memoria al usar el ejemplo de recorte de cadena, es necesario comprender los detalles internos de V8. Ciertas operaciones, como aplanar cadenas, pueden asignar un nuevo fragmento de memoria y liberar la memoria original. La biblioteca 'flat string' en NPM puede ayudar con esto al crear internamente una nueva cadena como un tipo de datos diferente. Es importante tener precaución al asignar cadenas para evitar problemas de memoria.

Quiero responder a la primera pregunta de uno de nuestros miembros de la audiencia. Es una pregunta de I Am. La pregunta es, ¿cómo manejarías mejor el ejemplo de recorte de cadena para evitar que la cadena original se mantenga en memoria? En este caso, debes conocer los detalles internos de V8, y comprender cómo funcionan estas cosas. Y hay algunas operaciones que aplanarían las cadenas. Por lo tanto, asignarían un nuevo fragmento de memoria completo para la ruta que se requiere en tu caso de uso. Y luego no habría una referencia sólida a la memoria original que asignaste, y luego se liberaría. Hay una biblioteca que en realidad hace eso como una operación muy simple. Originalmente, simplemente convertía la cadena a un número en el medio. Luego se convertiría internamente. Y trata de crear una nueva cadena internamente en V8. Simplemente usa un truco para hacer que esa cadena internamente sea un tipo de datos diferente. Y se llama flat string, escrito como F-L-A-T-S-T-R. Y puedes echarle un vistazo en NPM. Sí, exactamente. Puedes verlo allí y simplemente revisar algunos benchmarks. Pero en su mayoría, debes tener cuidado en cómo asignas cadenas para evitarlo en primer lugar. Trata de evitarlo, por supuesto. Pero en lugar de parchearlo más adelante.

QnA

Identificación de Fugas de Memoria y Herramientas

Short description:

Existen herramientas específicas para identificar fugas de memoria, que se pueden encontrar en el sitio web de Node.js. Al identificar una fuga de memoria, es importante monitorear tus recursos y utilizar herramientas como un APM para detectar las fugas. Para inspeccionar e identificar fugas de memoria en las funciones de AWS Lambda o Google Firebase, debes ejecutar el código en un entorno donde tengas control total y puedas crear volcados de montón. Se recomienda hacer esto en un entorno de preparación. Las pruebas automatizadas para fugas de memoria no son comunes, pero el monitoreo puede ayudar a identificar cuando el uso de memoria excede los límites esperados. Git bisect se puede utilizar para rastrear cambios que pueden haber causado fugas de memoria.

La siguiente pregunta es de CreoZot. ¿Existen herramientas específicas para identificar fugas de memoria que puedas recomendar? Así que mencioné la página web a la que podrías ir. Y hay muchas herramientas diferentes que puedes usar. Está en el sitio web de Node.js. Y te recomiendo encarecidamente que le eches un vistazo.

De acuerdo. La siguiente pregunta es de Alexey. ¿Cómo inspeccionar e identificar fugas de memoria en las funciones de AWS, Lambda, o Google Firebase? Debería ser similar, de todos modos. Siempre que tengas acceso a ello, debería serlo. Bueno, en primer lugar, debes identificar la fuga, que tienes una fuga. Eso es normalmente lo que se hace con el monitoreo también. Normalmente monitoreas tus recursos. Esa es la primera parte. Utilizas, por ejemplo, un APM para eso. Y tan pronto como identifiques que hay una fuga, quieres ejecutar el código en cualquier entorno en el que tengas control total y puedas introspectar. Y puedes, por ejemplo, crear siempre volcados de montón en cualquier entorno. Esa sería una forma de hacerlo. Esa sería una de las formas que podrías usar. Personalmente, prefiero los volcados de montón, y obviamente debes intentar no hacerlo en producción. Siempre intenta hacerlo en un entorno de preparación, si es posible. De acuerdo. La siguiente pregunta es de André Calazans. ¿Has visto o realizado pruebas automatizadas para fugas de memoria? ¿Y sabes si hay alguna forma de diferenciar fugas en vivo de fugas de memoria reales? No he visto pruebas reales para fugas de memoria, pero si lo monitoreas, normalmente deberías recibir una notificación tan pronto como en cualquier parte de tu aplicación la memoria se descontrole, porque normalmente establecerías un límite para la memoria que se espera que uses. Y tan pronto como alcance ese punto, deberías echar un vistazo más de cerca. Y luego tal vez volver a tu historial de Git y ver qué ha cambiado allí. Es un proceso manual a partir de ahí, pero al menos tienes una marca de tiempo. Algo salió mal después de esa implementación. Entonces, en este caso, podrías usar fácilmente git bisect. Y git bisect es una característica muy poderosa.

Identificación y Solución de Fugas de Memoria

Short description:

Escribe una prueba para desencadenar la fuga de memoria e identificar el código que la causa. Utiliza el método bisect para encontrar la confirmación que introdujo la fuga.

Entonces, escribe una prueba que desencadene la fuga de memoria. Así sabrás al menos qué la está causando en algún lugar, incluso si aún no la has identificado. Y luego quieres identificar el código. Así sabrás, bien, cuando ejecuto este código, y luego en algún momento, por ejemplo, la aplicación se bloquea después de unos segundos o algo así. Y entonces sabes que la fuga está ahí. Luego la bisectas, y eso es algo rítmico donde simplemente avanzas desde ambos lados. Y si no se bloquea, entonces sabrías que este código no es una fuga. Y así puedes bisectarlo hasta encontrar la confirmación exacta que introdujo la fuga. Inteligente.

Grandes Objetos JSON y Límites de Memoria

Short description:

¿Pueden los grandes objetos JSON almacenados en memoria causar fugas de memoria? Existe un límite máximo para cada tipo de dato. Para las cadenas de texto, es aproximadamente 2 elevado a la potencia de 28 caracteres. Superar este límite resultará en un error. Sin embargo, no tengo conocimiento de ningún límite para los objetos JSON. Afortunadamente, normalmente no alcanzo esos límites.

La siguiente pregunta es de Alexei nuevamente. ¿Se pueden almacenar grandes objetos JSON en memoria? Perdón. ¿Los grandes objetos JSON almacenados en memoria pueden causar fugas de memoria? La estructura JSON no es particularmente más propensa a causar fugas de memoria que cualquier otra estructura de datos. Así es. Pero creo que Alexei se refiere a si es un problema si un objeto JSON se vuelve demasiado grande. En general, hay un límite máximo para cada tipo de dato. Muy cierto. Por ejemplo, para las cadenas de texto, creo que, si recuerdo correctamente, es aproximadamente 2 elevado a la potencia de 28 caracteres que una cadena puede tener. Y tan pronto como se supera ese límite, se producirá un error. Y probablemente tendríamos ese caso si tienes el JSON como una cadena de texto. Y si lo tienes como un objeto. No conozco ningún límite en este caso. Puede que exista uno. Y definitivamente hay uno para algunos tipos de datos. Pero afortunadamente, normalmente no alcanzo esos límites.

Experiencia de Ruben con las Fugas de Memoria

Short description:

Ruben comparte su experiencia con las fugas de memoria, recordando una vez en la que tuvo que reiniciar una aplicación todos los días debido a una fuga de memoria. Menciona que en ese momento no pudo depurar la aplicación. Ruben también menciona que ha tenido la suerte de no encontrarse con ninguna fuga de memoria personal en los últimos años.

Qué suerte tienes. La última pregunta de la que tenemos tiempo. Y es una pregunta de George Turley. Dado que estamos hablando de fugas de memoria, ¿cuál fue la peor fuga de memoria con la que te encontraste? Y si pudiste encontrarla, ¿cómo la solucionaste si pudiste? La peor que encontré personalmente... ya fue hace un tiempo. Probablemente sea la que mencioné antes en la que tenía que reiniciar la aplicación todos los días. Estaba usando la aplicación, la estaba desarrollando, y no tenía la posibilidad de debugearla. Pero definitivamente fue la que más me molestó. Yo... Ya no estoy seguro de cuál fue mi propia fuga de memoria que solucioné. Tuve suerte en los últimos años y no me encontré personalmente con ninguna de esas. Mantengámoslo así, entonces, Ruben. Entonces, Ruben, quiero agradecerte mucho por tu charla y esta sesión informativa de preguntas y respuestas. Si las personas tienen más preguntas y quieren hablar sobre fugas de memoria, entonces estarás en tu sala de oradores de chat espacial, ¿verdad? Sí. Genial. Y espero con ansias eso. Muy bien, lo escucharon aquí primero, gente. Ruben va a hablar con ustedes en su chat espacial. Espero verte pronto en persona, tal vez, Ruben, y que tengas un buen día. Gracias. Igualmente. Adiós. Adiós. Adiós. Adiós. Adiós. Adiós.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Node Congress 2022Node Congress 2022
26 min
It's a Jungle Out There: What's Really Going on Inside Your Node_Modules Folder
Top Content
Do you know what’s really going on in your node_modules folder? Software supply chain attacks have exploded over the past 12 months and they’re only accelerating in 2022 and beyond. We’ll dive into examples of recent supply chain attacks and what concrete steps you can take to protect your team from this emerging threat.
You can check the slides for Feross' talk here.
Node Congress 2022Node Congress 2022
34 min
Out of the Box Node.js Diagnostics
In the early years of Node.js, diagnostics and debugging were considerable pain points. Modern versions of Node have improved considerably in these areas. Features like async stack traces, heap snapshots, and CPU profiling no longer require third party modules or modifications to application source code. This talk explores the various diagnostic features that have recently been built into Node.
You can check the slides for Colin's talk here. 
JSNation 2023JSNation 2023
22 min
ESM Loaders: Enhancing Module Loading in Node.js
Native ESM support for Node.js was a chance for the Node.js project to release official support for enhancing the module loading experience, to enable use cases such as on the fly transpilation, module stubbing, support for loading modules from HTTP, and monitoring.
While CommonJS has support for all this, it was never officially supported and was done by hacking into the Node.js runtime code. ESM has fixed all this. We will look at the architecture of ESM loading in Node.js, and discuss the loader API that supports enhancing it. We will also look into advanced features such as loader chaining and off thread execution.
JSNation Live 2021JSNation Live 2021
19 min
Multithreaded Logging with Pino
Top Content
Almost every developer thinks that adding one more log line would not decrease the performance of their server... until logging becomes the biggest bottleneck for their systems! We created one of the fastest JSON loggers for Node.js: pino. One of our key decisions was to remove all "transport" to another process (or infrastructure): it reduced both CPU and memory consumption, removing any bottleneck from logging. However, this created friction and lowered the developer experience of using Pino and in-process transports is the most asked feature our user.In the upcoming version 7, we will solve this problem and increase throughput at the same time: we are introducing pino.transport() to start a worker thread that you can use to transfer your logs safely to other destinations, without sacrificing neither performance nor the developer experience.

Workshops on related topic

Node Congress 2023Node Congress 2023
109 min
Node.js Masterclass
Top Content
Workshop
Have you ever struggled with designing and structuring your Node.js applications? Building applications that are well organised, testable and extendable is not always easy. It can often turn out to be a lot more complicated than you expect it to be. In this live event Matteo will show you how he builds Node.js applications from scratch. You’ll learn how he approaches application design, and the philosophies that he applies to create modular, maintainable and effective applications.

Level: intermediate
JSNation 2023JSNation 2023
104 min
Build and Deploy a Backend With Fastify & Platformatic
WorkshopFree
Platformatic allows you to rapidly develop GraphQL and REST APIs with minimal effort. The best part is that it also allows you to unleash the full potential of Node.js and Fastify whenever you need to. You can fully customise a Platformatic application by writing your own additional features and plugins. In the workshop, we’ll cover both our Open Source modules and our Cloud offering:- Platformatic OSS (open-source software) — Tools and libraries for rapidly building robust applications with Node.js (https://oss.platformatic.dev/).- Platformatic Cloud (currently in beta) — Our hosting platform that includes features such as preview apps, built-in metrics and integration with your Git flow (https://platformatic.dev/). 
In this workshop you'll learn how to develop APIs with Fastify and deploy them to the Platformatic Cloud.
Node Congress 2023Node Congress 2023
63 min
0 to Auth in an Hour Using NodeJS SDK
WorkshopFree
Passwordless authentication may seem complex, but it is simple to add it to any app using the right tool.
We will enhance a full-stack JS application (Node.JS backend + React frontend) to authenticate users with OAuth (social login) and One Time Passwords (email), including:- User authentication - Managing user interactions, returning session / refresh JWTs- Session management and validation - Storing the session for subsequent client requests, validating / refreshing sessions
At the end of the workshop, we will also touch on another approach to code authentication using frontend Descope Flows (drag-and-drop workflows), while keeping only session validation in the backend. With this, we will also show how easy it is to enable biometrics and other passwordless authentication methods.
Table of contents- A quick intro to core authentication concepts- Coding- Why passwordless matters
Prerequisites- IDE for your choice- Node 18 or higher
JSNation Live 2021JSNation Live 2021
156 min
Building a Hyper Fast Web Server with Deno
WorkshopFree
Deno 1.9 introduced a new web server API that takes advantage of Hyper, a fast and correct HTTP implementation for Rust. Using this API instead of the std/http implementation increases performance and provides support for HTTP2. In this workshop, learn how to create a web server utilizing Hyper under the hood and boost the performance for your web apps.
React Summit 2022React Summit 2022
164 min
GraphQL - From Zero to Hero in 3 hours
Workshop
How to build a fullstack GraphQL application (Postgres + NestJs + React) in the shortest time possible.
All beginnings are hard. Even harder than choosing the technology is often developing a suitable architecture. Especially when it comes to GraphQL.
In this workshop, you will get a variety of best practices that you would normally have to work through over a number of projects - all in just three hours.
If you've always wanted to participate in a hackathon to get something up and running in the shortest amount of time - then take an active part in this workshop, and participate in the thought processes of the trainer.
TestJS Summit 2023TestJS Summit 2023
78 min
Mastering Node.js Test Runner
Workshop
Node.js test runner is modern, fast, and doesn't require additional libraries, but understanding and using it well can be tricky. You will learn how to use Node.js test runner to its full potential. We'll show you how it compares to other tools, how to set it up, and how to run your tests effectively. During the workshop, we'll do exercises to help you get comfortable with filtering, using native assertions, running tests in parallel, using CLI, and more. We'll also talk about working with TypeScript, making custom reports, and code coverage.