2012-01-11 19 views
14

Al tratar de depurar fugas de memoria en NodeJS, me resulta bastante difícil (dada la falta de herramientas de creación de perfiles que conozco).ejemplos de cierres en node.js que causarían filtraciones de memoria

Pensé que volvería a lo básico y me aseguraré de entender cómo se crearía una pérdida de memoria específicamente en NodeJS. Estoy confundido acerca de los tipos de cierres que pueden causar pérdidas de memoria y no estoy seguro de qué necesita el recolector de basura para liberar esa memoria.

¿Podría darme algunos ejemplos de patrones básicos que causarían una pérdida de memoria en Node.js?

Respuesta

17

No es una "fuga" exactamente, pero esto puede ser una trampa común.

var fn = (function() { 
    var a = "super long string ..."; 
    var b = "useless value"; 
    var c = "Hello, World!"; 

    return function() { 
    return c; 
    }; 
})(); 

Esto devuelve una función que hace referencia a un ámbito, y se mantendrá en todas las var sola en ese ámbito, a pesar de que solamente uno de esos valores son necesarios. Esto da como resultado más uso de memoria de la que necesita, especialmente si su función usa una variable pequeña, pero hay grandes valores en ese ámbito que no necesita seguir consultando.


Cómo corregirlo?

La opción simple es anular las variables que no le interesan al final de su función. Las variables todavía están en el alcance, pero sus datos se liberarán.

var fn = (function() { 
    var a = "super long string ..."; 
    var b = "useless value"; 
    var c = "Hello, World!"; 

    // do stuff with a and b 

    a = b = null; 

    return function() { 
    return c; 
    }; 
})(); 

O puede romper cualquier cosa que utiliza temp vars en su propia función de lo que sus alcances pueden ser liberados. Esta es una mejor solución para un proyecto más grande.

var doSetup = function() { 
    var a = "super long string ..."; 
    var b = "useless value"; 
    // do stuff with a and b 
}; 

var fn = (function() { 
    doSetup(); 

    var c = "Hello, World!"; 

    return function() { 
    return c; 
    }; 
})(); 
+0

lo que si no volvía c pero no hizo referencia a ello como esto: ' fn var = (function() {var a = "cadena super largo ..."; var b = "valor inútil" ; var c = "¡Hola, mundo!"; función() { c = "algo más"; }; })(); ' – crickeys

+0

El punto es que si todavía se hace referencia a la función, el ámbito de cierre que la creó se guarda en la memoria. Lo que esa función realmente hace no tiene ningún efecto en ese hecho. –

+0

Entonces, ¿cómo arreglarías el código original que publicaste para asegurarte de que no se estaba filtrando más? – crickeys

-1

Desde: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml?showone=Closures#Closures

Una cosa a tener en cuenta, sin embargo, es que un cierre mantiene un puntero a su ámbito de inclusión. Como resultado, asociar un cierre a un elemento DOM puede crear una referencia circular y, por lo tanto, una pérdida de memoria. Por ejemplo, en el siguiente código:

function foo(element, a, b) { 
    element.onclick = function() { /* uses a and b */ }; 
} 

el cierre función mantiene una referencia al elemento, a, y b incluso si nunca utiliza elemento. Como elemento también guarda una referencia al cierre, tenemos un ciclo que no será limpiado por la recolección de basura. En estas situaciones, el código puede ser estructurado de la siguiente manera:

function foo(element, a, b) { 
    element.onclick = bar(a, b); 
} 

function bar(a, b) { 
    return function() { /* uses a and b */ } 
} 
+1

algo útil, pero no estoy interesado en nada con elementos DOM ya que este es Node.JS – crickeys

0
  1. Usted puede utilizar el nodo-inspector para aplicaciones de nodo de depuración con las herramientas de desarrolladores de Chrome.

  2. La respuesta aceptada acerca de las variables de cierre no utilizadas es incorrecta porque en los motores JS modernos, solo las variables a las que se hace referencia en la función interna se incluyen en el ámbito de cierre. El resto es basura recolectada automáticamente. En otras palabras, esta condición teórica nunca ocurrirá realmente en Nodo.

  3. Para un ejemplo real (y bastante común) usando expreso, puede crear middleware que carga un archivo en la memoria para cada solicitud y luego lanzar una excepción no controlada en la misma solicitud, atrapar la excepción lanzada y luego fallar salir del proceso

La excepción lanzada hará que los recursos de solicitud cargados a quedarse en lugar de ser limpiado al final del ciclo de petición/respuesta.

No salir del proceso cuando ocurre la excepción significa que en lugar de apagarse y reiniciarse por algo como PM2 o ​​Forever, Nodo ignorará el error y seguirá atendiendo nuevas solicitudes como si nada hubiera sucedido. Como los recursos no se están limpiando, el proceso consumirá más y más memoria a lo largo del tiempo hasta que reduzca el rendimiento de la máquina y, finalmente, se quede sin espacio para asignar nuevos recursos.

Eso obviamente tendría un impacto negativo en la experiencia del usuario.

Véase también Why Would an Exception Cause Resource Leaks in Node.js.

Cuestiones relacionadas