variable tipos memoria liberar javascript memory-management

tipos - ¿Cómo se asignan las variables de memoria en Javascript?



selector variable jquery (2)

Me gustaría saber cómo se asignan las variables locales a la memoria en javascript. En C y C ++, las variables locales se almacenan en la pila. ¿Es lo mismo en javascript? o todo está almacenado en montón?


Desafortunadamente, la respuesta es: depende.

Hubo un gran cambio en los motores de JavaScript recientes que comenzaron a optimizar mucho mejor de lo que solían hacerlo. La respuesta solía ser: "Las variables locales se almacenan en marcos de pila asignados en el montón para que los cierres funcionen". Ya no es tan simple.

Ha habido (o solía ser como hace 20-30 años) una investigación para las implementaciones de Scheme y la optimización de cierre (JavaScript heredó casi todos los cierres de Scheme, excepto las continuaciones que lo hacen aún más complicado).

No tengo los enlaces en papel listos, pero si no tienes un recolector de basura increíblemente eficiente, también debes usar la pila. La parte difícil es lidiar con cierres, que necesitan tener variables asignadas en el montón. Para eso se usan diferentes estrategias. El resultado es un híbrido donde:

  • al crear funciones, puede reducir significativamente el número de tramas asignadas en el montón asignadas / desasignadas
  • algunas variables pueden colocarse en la pila de forma segura, ya que su intervalo de tiempo es limitado (a menudo está conectado a las llamadas de función)
  • en algunos casos, usted sabe que puede estar creando un cierre, pero puede esperar hasta que eso suceda y luego asignar el montón de marco de pila para él y copiar los valores actuales de la pila
  • hay optimizaciones conectadas a tail-calls, donde puedes asignar el heap antes y luego reutilizar el frame de la pila para la siguiente llamada a la función, pero eso no se usa en los motores de javascript hasta donde sé actualmente

este campo está cambiando muy rápido en varios motores competidores, por lo que la respuesta probablemente será "depende"

Además, en las nuevas versiones del lenguaje, veremos características como let y const que en realidad facilitan a los motores optimizar las decisiones de asignación. Especialmente la inmutabilidad ayuda mucho, ya que puede copiar valores libremente de la pila (y hacer luego parte del objeto de cierre, por ejemplo) sin resolver colisiones de variables cambiantes de cierres diferentes.


En realidad es un área muy interesante de Javascript. Detalles en la especificación , pero: la forma en que JavaScript maneja las variables locales es bastante diferente de la forma en que C lo hace. Cuando llama a una función, entre otras cosas, se crea un "entorno variable" para esa llamada, que tiene algo llamado "objeto vinculante". (Llámalo el "objeto variable" para abreviar, diciendo "el objeto vinculante del entorno variable" es un poco largo!) El objeto variable tiene propiedades para los argumentos de la función, todas las variables locales declaradas en la función y todas las funciones declaradas dentro de la función (junto con un par de otras cosas). Las referencias no calificadas (por ej., El foo in foo , no obj.foo ) dentro de la función se verifican primero contra el objeto variable para ver si coinciden con las propiedades en él; si lo hacen, esas propiedades se utilizan.

Cuando un cierre sobrevive al retorno de la función (lo que puede suceder por varias razones), el objeto variable para esa llamada de función se retiene en la memoria por la referencia del cierre. A primera vista, eso sugeriría que la pila no se usa para variables locales; de hecho, los motores modernos de JavaScript son bastante inteligentes, y pueden (si es que vale la pena) usar la pila para los locales que realmente no son utilizados por el cierre. (Naturalmente, la pila se sigue utilizando para hacer un seguimiento de las direcciones de devolución y demás).

Aquí hay un ejemplo:

function foo(a, b) { var c; c = a + b; function bar(d) { alert("d * c = " + (d * c)); } return bar; } var b = foo(1, 2); b(3); // alerts "d * c = 9"

Cuando llamamos a foo , se crea un objeto variable con estas propiedades:

  • b : los argumentos para la función
  • c - una variable local declarada en la función
  • bar - una función declarada dentro de la función
  • (... y un par de otras cosas)

Cuando foo ejecuta la instrucción c = a + b; , hace referencia a las propiedades c , a , y b en el objeto variable para esa llamada a foo . Cuando foo devuelve una referencia a la función de la bar declarada dentro de ella, la bar sobrevive a la llamada a foo regresar. Como bar tiene una referencia (oculta) al objeto variable para esa llamada específica a foo , el objeto variable sobrevive (mientras que en el caso normal, no tendría referencias pendientes y estaría disponible para la recolección de basura).

Más tarde, cuando llamamos a la bar , se crea un nuevo objeto variable para esa llamada con (entre otras cosas) una propiedad llamada d - el argumento de la bar . Las referencias no calificadas dentro de la bar se verifican primero contra el objeto variable para esa llamada; por ejemplo, d resuelve la propiedad d en el objeto variable para la llamada a la bar . Pero una referencia no calificada que no coincide con una propiedad en su objeto variable se compara con el siguiente objeto variable en la "cadena de alcance" de la bar , que es el objeto variable para la llamada a foo . Y dado que tiene una propiedad c , esa es la propiedad utilizada dentro de la bar . Por ejemplo, en términos generales:

+----------------------------+ | `foo` call variable object | | -------------------------- | | a = 1 | | b = 2 | | c = 3 | | bar = (function) | +----------------------------+ ^ | chain | +----------------------------+ | `bar` call variable object | | -------------------------- | | d = 3 | +----------------------------+

Las implementaciones son libres de usar cualquier mecanismo que quieran debajo de las coberturas para hacer que lo anterior parezca suceder. Es imposible obtener acceso directo al objeto variable para una llamada de función, y la especificación deja en claro que está perfectamente bien si el objeto variable es simplemente un concepto, en lugar de una parte literal de la implementación. Una implementación simple puede hacer literalmente lo que dice la especificación; una más complicada puede usar una pila cuando no hay cierres involucrados (para el beneficio de velocidad), o siempre puede usar una pila, pero luego "arrancar" el objeto variable necesario para un cierre al reventar la pila. La única forma de saber en cualquier caso específico es mirar su código. :-)

Más acerca de los cierres, la cadena de alcance, etc. aquí: