2010-05-10 16 views
36

Vorrei sapere come vengono allocate le variabili locali in javascript. In C e C++ le variabili locali sono memorizzate nello stack. È lo stesso in javascript? o tutto è immagazzinato nell'heap?In che modo le variabili sono allocate in memoria in Javascript?

+0

possibile duplicato di [JavaScript ha un heap di memoria?] (Http://stackoverflow.com/questions/1026495/does-javascript-have-a-memory-heap) –

+2

Vedi questo articolo, [A Tour of V8 : object object] (http://www.jayconrod.com/posts/52/a-tour-of-v8-object-representation) che fornisce una panoramica di come il motore Javascript V8 rappresenta oggetti Javascript. –

+0

@RichardChambers: Grazie per il link piacevole. –

risposta

45

In realtà è un'area molto interessante di Javascript. Dettagli in the spec, ma: il modo in cui Javascript gestisce le variabili locali è molto diverso dal modo in cui C lo fa. Quando si chiama una funzione, tra le altre cose viene creato un "ambiente variabile" per quella chiamata, che ha qualcosa chiamato "oggetto vincolante". (Chiamalo "oggetto variabile" in breve, dicendo "l'oggetto vincolante dell'ambiente variabile" è solo un tad bit prolisso!) L'oggetto variabile ha proprietà per gli argomenti della funzione, tutte le variabili locali dichiarato nella funzione e tutte le funzioni dichiarate all'interno della funzione (insieme ad un paio di altre cose). I riferimenti non qualificati (ad esempio, il foo in foo, non obj.foo) all'interno della funzione sono prima verificata l'oggetto variabile per vedere se corrispondono proprietà su di esso; se lo fanno, vengono utilizzate quelle proprietà.

Quando una chiusura sopravvive alla funzione di ritorno (che può accadere per diversi motivi), l'oggetto variabile per quella chiamata di funzione è trattenuto nella memoria dal riferimento dalla chiusura. A prima vista, ciò suggerirebbe che lo stack non venga utilizzato per le variabili locali; infatti, i moderni motori JavaScript sono abbastanza intelligenti, e possono (se ne vale la pena) utilizzare lo stack per i locali che non vengono effettivamente utilizzati dalla chiusura. (Naturalmente, lo stack è ancora usato per tenere traccia di indirizzi di ritorno e così via.)

Ecco un esempio:

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" 

Quando chiamiamo foo, un oggetto variabile viene creato con queste proprietà:

  • a e b   — gli argomenti della funzione
  • c   — una variabile locale dichiarata nella funzione
  • bar   — una funzione dichiarata all'interno della funzione
  • (... e un paio di altre cose)

Quando foo esegue l'istruzione c = a + b;, è riferimento le c, a e b proprietà sull'oggetto variabile per tale chiamata a foo. Quando foo restituisce un riferimento alla funzione bar dichiarata al suo interno, bar sopravvive la chiamata a foo ritorno. Dal momento che ha un bar (nascosto) riferimento all'oggetto variabile per quella specifica chiamata a foo, l'oggetto variabile sopravvive (mentre nel caso normale, non avrebbe alcun riferimenti in sospeso e quindi sarebbe disponibile per la garbage collection).

Più tardi, quando chiamiamo bar, un nuovo oggetto variabile per tale chiamata viene creato con (tra le altre cose) una proprietà chiamata d   — l'argomento bar.I riferimenti non qualificati all'interno di bar vengono prima verificati rispetto all'oggetto variabile per quella chiamata; per esempio, d risolve la proprietà d sull'oggetto variabile per la chiamata a bar. Ma un riferimento non qualificato che non corrisponde a una proprietà sul suo oggetto variabile viene quindi confrontato con l'oggetto variabile successivo nella "catena di ambito" per bar, che è l'oggetto variabile per la chiamata a foo. E dal momento che ha una proprietà c, questa è la proprietà utilizzata all'interno di bar. Per esempio, in termini grezzi:

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

Implementazioni sono liberi di utilizzare qualsiasi meccanismo che vogliono sotto le coperte per fare il sopra sembrano per accadere. È impossibile ottenere un accesso diretto all'oggetto variabile per una chiamata di funzione, e la specifica chiarisce che è perfettamente soddisfacente se l'oggetto variabile è solo un concetto, piuttosto che una parte letterale dell'implementazione. Una semplice implementazione potrebbe benissimo fare letteralmente quello che dice la specifica; uno più complicato può usare una pila quando non ci sono chiusure coinvolte (per il beneficio della velocità), o può sempre usare una pila ma poi "strappare" l'oggetto variabile necessario per una chiusura quando fa scoppiare la pila. L'unico modo per sapere in un caso specifico è guardare il loro codice. :-)

Ulteriori su chiusura, la catena di portata, ecc qui:

+0

Grazie. Chiusura finita. – Anshul

+0

Cos'è un riferimento non qualificato? – LazerSharks

+1

@Gnuey: 'foo' in' foo' ma non in 'obj.foo', che è qualificato con' obj'. –

19

Purtroppo la risposta è: dipende.

C'è stato un grande cambiamento nei recenti motori javascript che hanno iniziato a ottimizzare molto meglio di prima. La risposta era: "Le variabili locali sono archiviate in frame stack allocati all'heap per far funzionare le chiusure". Non è più così semplice.

C'è stato (o usato come 20-30 anni fa) la ricerca per le implementazioni dello schema e l'ottimizzazione della chiusura (JavaScript ha ereditato praticamente le chiusure di Scheme, eccetto per le continuazioni che lo rendono ancora più complicato).

Non ho i collegamenti della carta pronti, ma se non si dispone di un garbage collector incredibilmente efficiente è necessario utilizzare anche lo stack. La parte delicata è quindi la gestione delle chiusure, che devono avere le allocazioni di heap delle variabili. Per questo si usano strategie diverse.Il risultato è un ibrido in cui:

  • da funzioni inline, è possibile ridurre il numero di fotogrammi heap allocata essere allocato/deallocato significativamente
  • alcune variabili possono essere messi in sicurezza sulla pila, in quanto è il momento della vita è limitato (è spesso collegato all'inallinare anche le chiamate di funzione)
  • in alcuni casi si sa che si potrebbe creare la chiusura, ma è possibile attendere fino a che ciò accada e quindi allocare lo stack stack-frame per esso e copiare i valori correnti dallo stack
  • ci sono ottimizzazioni connesse alle code-call, dove è possibile allocare l'heap in precedenza e quindi riutilizzarlo e stack frame per la prossima chiamata di funzione, ma non è usato nei motori javascript per quanto ne so attualmente

questo campo sta cambiando molto velocemente in diversi motori concorrenti, quindi la risposta probabilmente sarà ancora "dipende "

Inoltre, nelle nuove versioni del linguaggio vedremo funzionalità come let e const che rendono davvero più facile per i motori ottimizzare le decisioni di allocazione. Soprattutto l'immutabilità aiuta moltissimo, dal momento che è possibile copiare i valori liberamente dallo stack (e rendere quindi parte dell'oggetto di chiusura, ad esempio) senza risolvere le collisioni di variabili variabili da chiusure diverse.

+1

Grazie mille! Allora, dove posso imparare queste cose oltre a postare domande qui? Proviene dalla lettura di motori allo stato dell'arte (i loro documenti e persino il codice sorgente) o dallo scavare nei documenti di ricerca? Sono particolarmente interessato alle strategie di ottimizzazione che hai menzionato. Dove posso trovare i dettagli su di loro? Grazie ancora! – dacongy

+2

personalmente, il più influente per me è stata questa dissertazione di un guru dello schema Kent Dybvig http://www.cs.unm.edu/~williams/cs491/three-imp.pdf e ci sono alcuni documenti specialistici/dettagliati basati su in alto. Inoltre, ho visto di recente molte cose interessanti che descrivono gli attuali motori JavaScript e il progresso che i team stanno facendo in questo modo http://wingolog.org/archives/2011/07/05/v8-a-tale-of-two -compilatori ma di solito non vanno troppo in profondità. –

+0

il link originale (nella home page dell'autore) è http://www.cs.indiana.edu/~dyb/pubs/3imp.pdf –

Problemi correlati