recolección de basura con nodo.js


Tenía curiosidad sobre cómo el nodo.el patrón js de funciones anidadas funciona con el recolector de basura de v8. este es un ejemplo simple

readfile("blah", function(str) {
   var val = getvaluefromstr(str);
   function restofprogram(val2) { ... } (val)
})

Si restofprogram es de larga duración, ¿no significa eso que str nunca recogerá basura? Mi entendimiento es que con node terminas con funciones anidadas mucho. Esto obtiene basura recolectada si restofprogram fue declarado fuera, por lo que str no podría estar en el ámbito. Es esta una práctica recomendada?

EDIT No tenía la intención para complicar el problema. Eso fue descuido, así que lo he modificado.

Author: Vishnu, 2011-03-16

3 answers

Respuesta simple: si el valor de str no se hace referencia desde ningún otro lugar (y str en sí no se hace referencia desde restofprogram) se volverá inalcanzable tan pronto como regrese function (str) { ... }.

Detalles: El compilador V8 distingue las variables reales locales de las llamadas variables context capturadas por un cierre, sombreadas por una con-statement o una invocación eval.

Las variables locales viven en la pila y desaparecen tan pronto como se ejecuta la función completo.

Las variables de contexto viven en una estructura de contexto asignada al montón. Desaparecen cuando la estructura del contexto muere. Lo importante a tener en cuenta aquí es que las variables de contexto del mismo ámbito viven en la misma estructura . Permítanme ilustrarlo con un código de ejemplo:

function outer () {
  var x; // real local variable
  var y; // context variable, referenced by inner1
  var z; // context variable, referenced by inner2

  function inner1 () {
    // references context 
    use(y);
  }

  function inner2 () {
    // references context 
    use(z);
  }

  function inner3 () { /* I am empty but I still capture context implicitly */ } 

  return [inner1, inner2, inner3];
}

En este ejemplo, la variable x desaparecerá tan pronto como outer regrese, pero las variables y y z desaparecerán solo cuando ambas inner1, inner2 y inner3 muere. Esto sucede porque y y z se asignan en la misma estructura de contexto y los tres cierres hacen referencia implícita a esta estructura de contexto (incluso inner3 que no la usa explícitamente).

La situación se complica aún más cuando se empieza a usar con-sentencia, try/catch-sentencia que en V8 contiene una sentencia implícita con-sentencia dentro de la cláusula catch o global eval.

function complication () {
  var x; // context variable

  function inner () { /* I am empty but I still capture context implicitly */ }

  try { } catch (e) { /* contains implicit with-statement */ }

  return inner;
}

En este ejemplo x desaparecerá solo cuando inner morir. Porque:

  • try / catch - contiene implícito con - declaración en la cláusula catch
  • V8 asume que cualquier con - sombras de declaración todos los locales

Esto obliga a x a convertirse en una variable de contexto y inner captura el contexto para que x exista hasta que inner muera.

En general, si desea asegurarse de que la variable dada no retiene algún objeto durante más tiempo del que realmente necesita, puede fácilmente destruye este enlace asignando null a esa variable.

 63
Author: Vyacheslav Egorov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-17 18:11:16

En realidad su ejemplo es algo complicado. Fue a propósito? Parece que está enmascarando la variable externa val con un argumento interno de ámbito léxico restofprogram (), en lugar de usarlo realmente. Pero de todos modos, usted está preguntando acerca de str así que permítanme ignorar el truco de val en su ejemplo solo por el bien de la simplicidad.

Mi conjetura sería que la variable str no se recopilará antes de que finalice la función restofprogram (), incluso si no usa se. Si el restofprogram() no usa str y no usa eval() y new Function() entonces podría ser recolectado de manera segura, pero dudo que lo haga. Esta sería una optimización complicada para V8 probablemente no valga la pena. Si no hubiera eval y new Function() en el lenguaje entonces sería mucho más fácil.

Ahora, no tiene que significar que nunca se recopilaría porque cualquier controlador de eventos en un bucle de eventos de un solo subproceso debería terminar casi instantáneamente. De lo contrario, todo el proceso estaría bloqueado y tendría problemas más grandes que una variable inútil en la memoria.

Ahora me pregunto si no quisiste decir algo más que lo que realmente escribiste en tu ejemplo. Todo el programa en Node es igual que en el navegador: solo registra devoluciones de llamada de eventos que se activan de forma asíncrona más tarde después de que el cuerpo principal del programa ya haya terminado. Además, ninguno de los controladores está bloqueando, por lo que ninguna función está tomando un tiempo notable para terminar. No estoy seguro de si entendí lo que realmente quiso decir en su pregunta, pero espero que lo que he escrito sea útil para entender cómo funciona todo.

Actualización:

Después de leer más información en los comentarios sobre cómo se ve su programa, puedo decir más.

Si su programa es algo así como:

readfile("blah", function (str) {
  var val = getvaluefromstr(str);
  // do something with val
  Server.start(function (request) {
    // do something
  });
});

Entonces también puedes escribirlo así: {[18]]}

readfile("blah", function (str) {
  var val = getvaluefromstr(str);
  // do something with val
  Server.start(serverCallback);
});
function serverCallback(request) {
  // do something
});

Hará que el str salga del ámbito después del Servidor.start () se llama y eventualmente que te recojan. Además, hará que su sangría sea más manejable, lo que no debe subestimarse para programas más complejos.

En cuanto a la val podría convertirla en una variable global en este caso, lo que simplificaría enormemente su código. Por supuesto que no tiene que hacerlo, puede luchar con cierres, pero en este caso hacer que val sea global o hacerlo vivir en un ámbito externo común tanto para la devolución de llamada de readfile como para la función serverCallback parece lo más sencillo solución.

Recuerde que en todas partes cuando puede usar una función anónima también puede usar una función con nombre, y con ellas puede elegir en qué ámbito desea que vivan.

 4
Author: rsp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-30 10:05:45

Mi conjetura es que str NO será basura recolectada porque puede ser usada por restofprogram(). Sí, y str debería obtener GCed si restofprogram fue declarado fuera, excepto, si haces algo como esto:

function restofprogram(val) { ... }

readfile("blah", function(str) {
  var val = getvaluefromstr(str);
  restofprogram(val, str);
});

O si getvaluefromstr se declara como algo así:

function getvaluefromstr(str) {
  return {
    orig: str, 
    some_funky_stuff: 23
  };
}

Pregunta de seguimiento: ¿Hace v8 simplemente GC plain'ol o hace una combinación de GC y ref. contando (como python?)

 1
Author: dhruvbird,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-30 11:40:19