Cómo verificar usted mismo su código NodeJS en busca de pérdidas de memoria
Primero, veamos un caso simple de pérdida de memoria
var http = require('http');var server = http.createServer(function (req, res) {
for (var i=0; i<1000; i++ ) {
server.on('solicitud', función fugasyfunc() {});
}
res.end('Hola mundo\n'; }).listen(1337, '127.0.0.1');server.setMaxListen('Hola mundo\n', '127.0.0.1' ) ;fin: viernes 02 de enero de 2015 10:38:50 GMT+0000 (GMT),
crecimiento: 7620560,
motivo: 'crecimiento del montón durante 5 GC consecutivos (1 s) - 2147483648 bytes/hr '}
¡memwatch encontró una pérdida de memoria! Las reglas que usa memwatch para determinar cuándo ocurre una pérdida de memoria son las siguientes:
Cuando su memoria dinámica tiene 5 recolecciones de basura consecutivas Cuando el ciclo continúe creciendo, se solucionarán las pérdidas de memoria
Para obtener más información, consulte memwatch
Análisis de pérdidas de memoria
Utilizando memwatch, descubrimos pérdidas de memoria , eso es genial, pero ¿ahora qué? También necesitamos encontrar dónde ocurre realmente la pérdida de memoria. Para hacer esto, hay dos maneras de hacerlo: memwatch heap diff >Usando memwatch puedes obtener la diferencia entre el uso de la memoria del montón. el uso de memoria mientras el programa se está ejecutando.
Puede encontrar documentación detallada aquí
Por ejemplo, podemos realizar un volcado de montón en el intervalo de tiempo entre dos eventos de fuga:
var hd;memwatch.on('leak', function (información) {
console.error(info);
if (!hd) {
hd = new memwatch.HeapDiff();
hd = null;
}});
Al ejecutar este código se generará más información:
{ antes: { p>
nodos: 244023,
hora: viernes 02 de enero de 2015 12:13:11 GMT+0000 (GMT),
tamaño_bytes: 22095800,
tamaño: '21.07 mb' },
después: {
nodos: 280028,
hora: viernes 02 de enero de 2015 12: 13:13 GMT+0000 (GMT),
tamaño_bytes: 24689216,
tamaño: '23,55 mb' },
cambio: {
tamaño_bytes: 2593416,
tamaño: '2,47 MB',
nodos_liberados.388,
nodos_asignados: 36393,
detalles:
[ { size_bytes: 0,
'+': 0,
qué: ' (Reubicable)',
'-': 1,
tamaño: '0 bytes' },
{ size_bytes: 0,
'+': 1,
qué: 'Argumentos',
'-':1,
tamaño: '0 bytes' },
{ size_bytes .2856,
'+': 223,
qué: 'Matriz',
'-': 201,
tamaño: '2,79 kb' },
{ size_bytes: 2590272,
'+': 35987,
qué: 'Cierre',
' -': 11,
size: '2.47 mb' },...
Entonces, entre eventos de pérdida de memoria, vemos que la memoria del montón creció en 2,47 MB y el culpable causó El crecimiento de la memoria son cierres. Si su fuga es causada por una clase, el campo qué puede generar un nombre de clase específico, por lo que en ese caso tendrá suficiente información para ayudarlo a determinar en última instancia dónde está la fuga.
Sin embargo, en nuestro ejemplo, la única información que obtuvimos fue que la fuga provenía de un cierre, lo cual es muy útil, pero aún no suficiente para encontrar rápidamente la fuente del problema en aplicaciones complejas (aplicaciones complejas Los programas suelen tener muchos cierres y es difícil saber qué cierre provocó la pérdida de memoria)
Entonces, ¿qué debemos hacer? Aquí es donde entra en juego Heapdump.
Heapdump
El módulo npm node-heapdump es un módulo excelente que se puede usar para volcar el contenido de la memoria del montón del motor v8 para que puedas usarlo en el desarrollador del navegador Chrome. herramientas Vea cuál es el problema. Puede comparar instantáneas de memoria dinámica de diferentes etapas de ejecución en las herramientas de desarrollador, lo que puede ayudarle a identificar la ubicación de las pérdidas de memoria.
Para aprender más sobre heapdump puedes leer este artículo
Ahora intentemos usar heapdump, cada vez que encontramos una pérdida de memoria, escribimos una instantánea de la pila de memoria en ese punto en el disco:
memwatch.on('fuga', función(info) {
console.error(info);
var file = '/tmp/myapp-' + proceso .pid + '-' + Date.now() + '.heapsnapshot';
heapdump.writeSnapshot(archivo,función(err){
if (err) console.error (err );
else console.error('Wrote snapshot: ' + file);
});});
Al ejecutar nuestro código se generarán algunos archivos . archivos heapsnapshot en el disco en el directorio /tmp. Ahora, inicie las herramientas de desarrollador en Chrome (el acceso directo es alt+cmd+i en mac), haga clic en la pestaña Perfiles y presione el botón Cargar para cargar la instantánea.
Pudimos identificar claramente el Leakyfunc() original como el culpable de la pérdida de memoria.
Aún podemos identificar pérdidas de memoria más rápido comparando las diferencias entre los dos conjuntos de datos del montón en los dos registros:
Para obtener más información sobre la función de creación de perfiles de memoria de las herramientas de desarrollador. Para obtener más información, lea Domar al unicornio: artículo.
Turbo Test Runner
Hemos enviado un pequeño parche a FeedHenry, una herramienta de prueba desarrollada por Turbo, que utiliza la técnica de comprobación de pérdida de memoria descrita anteriormente. Esto permitirá a los desarrolladores escribir pruebas unitarias dirigidas a la memoria y, si un módulo tiene problemas de memoria, los resultados de la prueba generarán las advertencias adecuadas. Para obtener más información sobre los detalles, visite Módulos Turbo.
Conclusiones y otros detalles
Lo anterior analizó los métodos básicos para detectar pérdidas de memoria en NodeJS, aquí hay algunas conclusiones:
Heapdump tiene muchas subvulnerabilidades, como el tamaño de la instantánea, etc. Lea atentamente la documentación; la generación de instantáneas también consume más recursos de CPU.
Existen otras formas de generar instantáneas, cada una con sus pros y sus contras, así que elija la que mejor se adapte a su proyecto. (por ejemplo, para enviar sigusr2 a un proceso, etc., hay un proyecto memwatch-sigusr2 aquí)
Considere bajo qué circunstancias desea activar memwatch/heapdump, actívelos solo en un entorno de prueba. y considere la frecuencia del volcado de memoria para evitar agotar los recursos de la CPU. En resumen, elija el método que mejor se adapte a su proyecto.
Existen otras formas de detectar el crecimiento de la memoria, como monitorear directamente Process.memoryUsage().
Cuando se detecta un problema de memoria, primero debe determinar si efectivamente se trata de una pérdida de memoria antes de notificar al personal correspondiente.
Cuidado con los errores de cálculo; los breves picos en el uso de la memoria se comportan de manera muy similar a las pérdidas de memoria. Si su aplicación consume repentinamente una gran cantidad de CPU y memoria, su tiempo de procesamiento puede abarcar múltiples ciclos de recolección de basura, en cuyo caso memwatch probablemente la caracterizará falsamente como una pérdida de memoria. Sin embargo, en este caso, una vez que su aplicación agote estos recursos, el consumo de memoria volverá a niveles normales. Por lo tanto, debe prestar atención a las pérdidas de memoria que se informan persistentemente y puede ignorar una o dos alertas repentinas.
Actualmente, memwatch solo admite el nodo 0.10.x, las versiones compatibles del nodo 0.12.x (y posiblemente io.js) se encuentran en esta rama