posible fuga de memoria de EventEmitter detectada

231

Recibo la siguiente advertencia:

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.
Trace: 
    at EventEmitter.<anonymous> (events.js:139:15)
    at EventEmitter.<anonymous> (node.js:385:29)
    at Server.<anonymous> (server.js:20:17)
    at Server.emit (events.js:70:17)
    at HTTPParser.onIncoming (http.js:1514:12)
    at HTTPParser.onHeadersComplete (http.js:102:31)
    at Socket.ondata (http.js:1410:22)
    at TCP.onread (net.js:354:27)

Escribí un código como este en server.js:

http.createServer(
    function (req, res) { ... }).listen(3013);

Cómo arreglar esto ?

Riz
fuente
46
Use process.on('warning', e => console.warn(e.stack));para depurar la advertencia. No lo use process.setMaxListeners(0);ya que la advertencia está ahí por alguna razón.
Shwetabh Shekhar
Gracias. Instrucción muy útil.
Abdullah Al Farooq
Este error me sucede el yarn install. ¿Dónde puedo poner esta línea para agregar seguimiento de pila?
Sonic Soul

Respuestas:

94

Esto se explica en la documentación del nodo eventEmitter

¿Qué versión de Node es esta? ¿Qué otro código tienes? Ese no es un comportamiento normal.

En resumen, es: process.setMaxListeners(0);

Ver también: node.js - request - ¿Cómo “emitter.setMaxListeners ()”?

Corey Richardson
fuente
1
v0.6.11 ... Hice todo, pero la advertencia sigue ahí. :(
Riz
55
Estoy usandoprocess.on('uncaughtException', callback);
Riz
99
process.setMaxListeners(0); // OMG, its so simple... :D
Riz
11
No eliminaría el límite máximo de oyentes. No recibirá advertencias, pero obtendrá pérdidas de memoria.
15
¿Cómo obtuvo esta respuesta todos estos votos y fue elegida como respuesta correcta? aunque debería funcionar, ¡pero esto está completamente mal!
ProllyGeek
205

Me gustaría señalar aquí que esa advertencia está ahí por una razón y hay una buena posibilidad de que la solución correcta no aumente el límite, sino que descubra por qué está agregando tantos oyentes al mismo evento. Solo aumente el límite si sabe por qué se están agregando tantos oyentes y está seguro de que es lo que realmente desea.

¡Encontré esta página porque recibí esta advertencia y en mi caso había un error en algún código que estaba usando que estaba convirtiendo el objeto global en un EventEmitter! Sin duda aconsejaría no aumentar el límite globalmente porque no desea que estas cosas pasen desapercibidas.

voltrevo
fuente
14
+1. Convenido. La advertencia indica un posible estado de fuga y el aumento irreflexivo de maxListeners no necesariamente solucionará el problema. jongleberry.com/understanding-possible-eventemitter-leaks.html
Jeremiah Adams
3
¿Cómo puede depurar "Advertencia: se detectó una posible fuga de memoria de EventEmitter. Se agregaron 11 escuchas de error. Utilice emitter.setMaxListeners () para aumentar el límite". Qué deberíamos estar buscando?
Phil
2
Pero no hay seguimiento de la pila ni código en ninguna parte con ese mensaje de error. Obtengo mayúsculas W y P en "Advertencia" y "Posible", así que creo que podría ser un error diferente. Necesito escuchar más de un evento, pero solo llamo a .on una vez en todos los casos, así que no estoy seguro de cuál es el problema.
Phil
2
@ Phil_1984_ ¿Has encontrado una solución? si no parece que esto funciona - stackoverflow.com/questions/38482223/…
Yoni Jah
3
FYI, el enlace del primer comentario (jongleberry.com) está fuera de línea. Aquí está la versión archivada: web.archive.org/web/20180315203155/http://www.jongleberry.com/…
Jeff Ward,
76

De manera predeterminada, se puede registrar un máximo de 10 oyentes para cualquier evento individual.

Si es su código, puede especificar maxListeners a través de:

const emitter = new EventEmitter()
emitter.setMaxListeners(100)
// or 0 to turn off the limit
emitter.setMaxListeners(0)

Pero si no es su código, puede usar el truco para aumentar el límite predeterminado globalmente:

require('events').EventEmitter.prototype._maxListeners = 100;

Por supuesto, puede desactivar los límites, pero tenga cuidado:

// turn off limits by default (BE CAREFUL)
require('events').EventEmitter.prototype._maxListeners = 0;

Por cierto. El código debe estar al comienzo de la aplicación.

AGREGAR: desde el nodo 0.11, este código también funciona para cambiar el límite predeterminado:

require('events').EventEmitter.defaultMaxListeners = 0
zag2art
fuente
55
Esta fue la única solución que funcionó para mí en el Nodo 5.6.0. ¡Gracias una tonelada!
Andrew Faulkner
Estoy usando react-native, nodo versión 8. *. *. Esto no funcionó para mí.
Thomas Valadez
el mío era require ('eventos'). EventEmitter.defaultMaxListeners = Infinity;
Karl Anthony Baluyot
73

La respuesta aceptada proporciona la semántica sobre cómo aumentar el límite, pero como @voltrevo señaló que la advertencia está ahí por una razón y su código probablemente tenga un error.

Considere el siguiente código con errores:

//Assume Logger is a module that emits errors
var Logger = require('./Logger.js');

for (var i = 0; i < 11; i++) {
    //BUG: This will cause the warning
    //As the event listener is added in a loop
    Logger.on('error', function (err) {
        console.log('error writing log: ' + err)
    });

    Logger.writeLog('Hello');
}

Ahora observe la forma correcta de agregar el oyente:

//Good: event listener is not in a loop
Logger.on('error', function (err) {
    console.log('error writing log: ' + err)
});

for (var i = 0; i < 11; i++) {
    Logger.writeLog('Hello');
}

Busque problemas similares en su código antes de cambiar maxListeners (que se explica en otras respuestas)

Rayee Roded
fuente
13
esta respuesta debe aceptarse ya que muestra la razón real detrás de la advertencia y cómo resolverla, +1
Ganesh Karewad el
¡Esta es la respuesta correcta! Sinceramente, creo que la advertencia maxListener aparece principalmente debido a algunos códigos con errores. En mi caso fue el código mysql. Trataré de responder solo para aclarar eso.
Adrian
25

Reemplazar .on()con once(). Utilizandoonce() elimina oyentes de eventos cuando el evento es manejado por la misma función.

Si esto no lo soluciona, vuelva a instalar restler con esto en su package.json "restler": "git: //github.com/danwrong/restler.git#9d455ff14c57ddbe263dbbcd0289d76413bfe07d"

Esto tiene que ver con el restler 0.10 portándose mal con el nodo. puedes ver el problema cerrado en git aquí: https://github.com/danwrong/restler/issues/112 Sin embargo, npm aún no ha actualizado esto, por eso debes consultar el encabezado de git.

Davis Dulin
fuente
esto corrige este error en mi código usando el marco Puppeterr
C Alonso C Ortega
4

Versión de nodo: v11.10.1

Mensaje de advertencia del seguimiento de la pila:

process.on('warning', e => console.warn(e.stack));
(node:17905) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 wakeup listeners added. Use emitter.setMaxListeners() to increase limit
MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 wakeup listeners added. Use emitter.setMaxListeners() to increase limit
    at _addListener (events.js:255:17)
    at Connection.addListener (events.js:271:10)
    at Connection.Readable.on (_stream_readable.js:826:35)
    at Connection.once (events.js:300:8)
    at Connection._send (/var/www/html/fleet-node-api/node_modules/http2/lib/protocol/connection.js:355:10)
    at processImmediate (timers.js:637:19)
    at process.topLevelDomainCallback (domain.js:126:23)

Después de buscar problemas de github, documentación y crear fugas de memoria de emisor de eventos similares, este problema se observó debido al módulo node-apn utilizado para la notificación push de iOS.

Esto lo resolvió:

Solo debe crear un Proveedor por proceso para cada certificado / par de claves que tenga. No necesita crear un nuevo proveedor para cada notificación. Si solo envía notificaciones a una aplicación, no hay necesidad de más de un proveedor.

Si constantemente está creando instancias de proveedor en su aplicación, asegúrese de llamar a Provider.shutdown () cuando haya terminado con cada proveedor para liberar sus recursos y memoria.

Estaba creando un objeto de proveedor cada vez que se enviaba la notificación y esperaba que el gc la borrara.

PC Sandeep
fuente
2

En mi caso, era lo child.stderr.pipe(process.stderr)que se llamaba cuando estaba iniciando 10 (más o menos) instancias del niño. Entonces, cualquier cosa, que lleve a adjuntar un controlador de eventos al mismo objeto EventEmitter en un LOOP, hace que nodejs arroje este error.

Vikas Gautam
fuente
2

¡Algunas veces estas advertencias ocurren cuando no es algo que hemos hecho, sino algo que hemos olvidado hacer!

Encontré esta advertencia cuando instalé el paquete dotenv con npm, pero fui interrumpido antes de agregar la declaración require ('dotenv'). Load () al comienzo de mi aplicación. Cuando volví al proyecto, comencé a recibir las advertencias "Posible evento de fuga de memoria detectada en el emisor".

¡Supuse que el problema era de algo que había hecho, no de algo que no había hecho!

Una vez que descubrí mi descuido y agregué la declaración require, se borró la advertencia de pérdida de memoria.

Rotar
fuente
2

Prefiero buscar y solucionar problemas en lugar de suprimir registros siempre que sea posible. Después de un par de días de observar este problema en mi aplicación, me di cuenta de que estaba configurando oyentes en el req.socketmiddleware Express para detectar errores de socket io que seguían apareciendo. En algún momento, aprendí que eso no era necesario, pero de todos modos mantuve a los oyentes cerca. Simplemente los eliminé y el error que estás experimentando desapareció. Verifiqué que era la causa al ejecutar solicitudes a mi servidor con y sin el siguiente middleware:

socketEventsHandler(req, res, next) {
        req.socket.on("error", function(err) {
            console.error('------REQ ERROR')
            console.error(err.stack)
        });
        res.socket.on("error", function(err) {
            console.error('------RES ERROR')
            console.error(err.stack)
        });
        next();
    }

La eliminación de ese middleware detuvo la advertencia que está viendo. Buscaría su código e intentaría encontrar cualquier lugar en el que esté configurando oyentes que no necesita.

lwdthe1
fuente
1

Estaba teniendo el mismo problema. y el problema fue causado porque estaba escuchando el puerto 8080, en 2 oyentes.

setMaxListeners() funciona bien, pero no lo recomendaría.

la forma correcta es verificar su código en busca de oyentes adicionales, eliminar el oyente o cambiar el número de puerto en el que está escuchando, esto solucionó mi problema.

Noman Abid
fuente
1

Estaba teniendo esto hasta hoy cuando empiezo grunt watch. Finalmente resuelto por

watch: {
  options: {
    maxListeners: 99,
    livereload: true
  },
}

El mensaje molesto se ha ido.

Ariful Haque
fuente
1

Debe borrar todos los oyentes antes de crear nuevos usando:

Servidor de cliente

socket.removeAllListeners(); 

Suponiendo que el socket es el socket de su cliente / o el socket del servidor creado.

También puede suscribirse desde oyentes de eventos específicos, como por ejemplo eliminar al connectoyente de esta manera:

this.socket.removeAllListeners("connect");
ProllyGeek
fuente
0

Dijiste que estás usando process.on('uncaughtException', callback);
¿Dónde estás ejecutando esta declaración? ¿Está dentro de la devolución de llamada pasada a http.createServer?
En caso afirmativo, se adjuntará una copia diferente de la misma devolución de llamada al evento uncaughtException en cada nueva solicitud, porque function (req, res) { ... }se ejecuta cada vez que entra una nueva solicitud y también lo hará la declaración process.on('uncaughtException', callback);
Tenga en cuenta que el objeto de proceso es global para todas sus solicitudes y agrega oyentes para su evento cada vez que llega una nueva solicitud no tendrá ningún sentido. Es posible que no desee ese tipo de comportamiento.
En caso de que desee adjuntar un nuevo oyente para cada nueva solicitud, debe eliminar todos los oyentes anteriores adjuntos al evento, ya que ya no serían necesarios utilizando:
process.removeAllListeners('uncaughtException');

Monish Chhadwa
fuente
0

La solución de nuestro equipo para esto fue eliminar una ruta de registro de nuestro .npmrc. Teníamos dos alias de ruta en el archivo rc, y uno apuntaba a una instancia de Artifactory que había quedado en desuso.

El error no tiene nada que ver con el código real de nuestra aplicación, pero todo que ver con nuestro entorno de desarrollo.

RossO
fuente
0

Estaba enfrentando el mismo problema, pero lo he manejado con éxito con async en espera.
Por favor, compruebe si ayuda.

let dataLength = 25;
Antes:
  for (let i = 0; i <dataLength; i ++) {
      sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }

Después:
  for (let i = 0; i <dataLength; i ++) {
      await sftp.get (remotePath, fs.createWriteStream ( xyzProject/${data[i].name}));
  }

Vivek Mehta
fuente
0

Gracias a RLaaa por darme una idea de cómo resolver el problema real / la causa raíz de la advertencia. Bueno, en mi caso fue el código de error MySQL.

Siempre que haya escrito una Promesa con un código como este:

pool.getConnection((err, conn) => {

  if(err) reject(err)

  const q = 'SELECT * from `a_table`'

  conn.query(q, [], (err, rows) => {

    conn.release()

    if(err) reject(err)

    // do something
  })

  conn.on('error', (err) => {

     reject(err)
  })
})

Observe que hay un conn.on('error')oyente en el código. Ese código que literalmente agrega oyente una y otra vez depende de cuántas veces llame a la consulta. Mientras tanto if(err) reject(err)hace lo mismo.

Así que eliminé el conn.on('error')oyente y listo ... ¡resuelto! Espero que esto te ayude.

Adrian
fuente
-4

Ponga esto en la primera línea de su server.js (o lo que sea que contenga su aplicación principal Node.js):

require('events').EventEmitter.prototype._maxListeners = 0;

y el error desaparece :)

Sebastian
fuente
Me diste una idea para ponerlo en un archivo principal, y funcionó. Solo lo estaba poniendo en un lugar equivocado. ¡Gracias!
sklimkovitch