2010-05-10 22 views
36

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?¿Cómo se asignan las variables de memoria en Javascript?

+0

posible duplicado de [¿El Javascript tiene un montón de memoria?] (Http://stackoverflow.com/questions/1026495/does-javascript-have-a-memory-heap) –

+2

Ver este artículo, [Un recorrido por el V8 : representación de objetos] (http://www.jayconrod.com/posts/52/a-tour-of-v8-object-representation) que proporciona una descripción general de cómo el motor de Javascript V8 representa los objetos de Javascript. –

+0

@RichardChambers: Gracias por el buen enlace. –

Respuesta

45

En realidad, es un área muy interesante de Javascript. Detalles en the spec, 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 que "el objeto vinculante del entorno variable" es solo un tad poco prolijo!) El objeto variable tiene propiedades para los argumentos de la función, todas las variables locales declarado 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 ejemplo, foo en 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 (que puede suceder por varias razones), el objeto variable para esa llamada de función es retenido 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 todavía se utiliza para hacer el seguimiento de direcciones de retorno y tal.)

He aquí 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 foo, un objeto variable se crea con estas propiedades:

  • a y b   — los argumentos de 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 sentencia c = a + b;, se hace referencia a las propiedades c, a y b en el objeto variable para esa llamada al foo. Cuando foo devuelve una referencia a la función bar declarada en su interior, bar sobrevive a la llamada a foo que regresa. Dado que bar tiene una referencia (oculta) al objeto variable para esa llamada específica al 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 bar, un nuevo objeto variable para esa llamada se crea con (entre otras cosas) una propiedad llamada d   — el argumento a bar.Las referencias no calificadas dentro de bar se verifican primero contra el objeto variable para esa llamada; por ejemplo, d se resuelve en la propiedad d en el objeto variable para la llamada al 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" para bar, que es el objeto variable para la llamada a foo. Y como tiene una propiedad c, esa es la propiedad utilizada en bar. Por ejemplo, en términos ásperos:

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

Implementaciones son libres de usar cualquier mecanismo que quieren bajo las sábanas para hacer lo anterior parecen a 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 sobre dispositivos de cierre, la cadena de ámbito, etc. aquí:

+0

Gracias. Cierre finalmente entendido. – Anshul

+0

¿Qué es una referencia no calificada? – LazerSharks

+1

@Gnuey: El 'foo' en' foo' pero no en 'obj.foo', que está calificado con' obj'. –

19

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) investigación para implementaciones de esquemas y 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 listos los enlaces de papel, pero si no tiene un recolector de basura increíblemente eficiente, también necesita 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:

  • por las funciones de procesos en línea, se puede reducir el número de fotogramas montón asignados siendo asignado/desasignado significativamente
  • algunas variables se pueden poner con seguridad en la pila, ya que es lapso de tiempo es limitado (a menudo está conectado a las llamadas a funciones tambié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 desde la pila
  • hay optimizaciones conectadas a las llamadas finales, donde puede asignar el heap antes y luego reutilizar e stack frame para la siguiente llamada a función, pero eso no se usa en los motores javascript hasta ahora

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

Además, en las nuevas versiones del lenguaje veremos características como let y const que realmente hacen que sea más fácil para 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.

+1

¡Muchas gracias! Entonces, ¿dónde puedo aprender estas cosas además de publicar preguntas aquí? ¿Es por leer motores de vanguardia (sus documentos e incluso su código fuente) o por indagar en trabajos de investigación? Estoy particularmente interesado en las estrategias de optimización que mencionaste. ¿Dónde puedo encontrar detalles sobre ellos? ¡Gracias de nuevo! – dacongy

+2

personalmente, la más influyente para mí fue esta disertación de un gurú de esquema Kent Dybvig http://www.cs.unm.edu/~williams/cs491/three-imp.pdf y hay algunos documentos más especializados/detallados que se basan en encima de eso. Además, recientemente vi muchas cosas interesantes que describen los motores actuales de JavaScript y el progreso que están logrando los equipos como este http://wingolog.org/archives/2011/07/05/v8-a-tale-of-two -compiladores pero generalmente no van muy profundo. –

+0

el enlace original (en la página principal del autor) es http://www.cs.indiana.edu/~dyb/pubs/3imp.pdf –

Cuestiones relacionadas