Che cos'è la garbage collection JavaScript?


Risposte:


192

Eric Lippert ha scritto un post di blog dettagliato su questo argomento qualche tempo fa (confrontandolo inoltre con VBScript ). Più precisamente, ha scritto su JScript , che è l'implementazione di Microsoft di ECMAScript, sebbene molto simile a JavaScript. Immagino che si possa presumere che la stragrande maggioranza dei comportamenti sarebbe la stessa per il motore JavaScript di Internet Explorer. Naturalmente, l'implementazione varierà da browser a browser, anche se sospetto che potresti prendere una serie di principi comuni e applicarli ad altri browser.

Citato da quella pagina:

JScript utilizza un garbage collector mark-and-sweep non generazionale. Funziona così:

  • Ogni variabile che è "nell'ambito" è chiamata "scavenger". Uno scavenger può riferirsi a un numero, un oggetto, una stringa, qualunque cosa. Manteniamo un elenco di scavenger - le variabili vengono spostate nell'elenco scav quando entrano nell'ambito e fuori dall'elenco scav quando escono dall'ambito.

  • Ogni tanto viene eseguito il garbage collector. Innanzitutto mette un "segno" su ogni oggetto, variabile, stringa, ecc. - Tutta la memoria tracciata dal GC. (JScript utilizza internamente la struttura di dati VARIANT e ci sono molti bit inutilizzati in più in quella struttura, quindi ne abbiamo appena impostato uno.)

  • In secondo luogo, cancella il segno sugli spazzini e sulla chiusura transitiva dei riferimenti degli spazzini. Quindi se un oggetto scavenger fa riferimento a un oggetto non scavenger, allora cancelliamo i bit sul non scavenger e su tutto ciò a cui si riferisce. (Sto usando la parola "chiusura" in un senso diverso rispetto al mio post precedente.)

  • A questo punto sappiamo che tutta la memoria ancora contrassegnata è allocata memoria che non può essere raggiunta da nessun percorso da nessuna variabile nell'ambito. Tutti quegli oggetti sono istruiti a demolirsi, il che distrugge qualsiasi riferimento circolare.

Lo scopo principale della garbage collection è di consentire al programmatore di non preoccuparsi della gestione della memoria degli oggetti che creano e usano, anche se ovviamente a volte non è possibile evitarlo - è sempre utile avere almeno un'idea approssimativa di come funziona la garbage collection .

Nota storica: una precedente revisione della risposta aveva un riferimento errato deleteall'operatore. In JavaScript l' deleteoperatore rimuove una proprietà da un oggetto ed è completamente diverso da deletein C / C ++.


27
la guida di Apple è difettosa: l'autor utilizza in deletemodo errato; ad esempio nel primo esempio, invece di delete foo, devi prima rimuovere il listener di eventi tramite window.removeEventListener()e poi usare foo = nullper sovrascrivere la variabile; in IE, delete window.foo(ma non delete foo) avrebbe funzionato anche se foofosse globale, ma anche allora non avrebbe funzionato in FF o Opera
Christoph,

3
Essere consapevoli del fatto che l'articolo di Eric dovrebbe essere considerato "solo a fini storici". Ma è ancora informativo.
Peter Ivan,

2
Nota anche: IE 6 e 7 NON utilizzano un garbage collector mark-and-sweep non generazionale. Usano un semplice riferimento che conta il Garbage Collector, che è più vulnerabile ai problemi di riferimento circolare con Garbage Collection.
Doug,

1
ECMAScript deleteè un operatore unario (un'espressione), non un'affermazione (cioè:) delete 0, delete 0, delete 3. Sembra una dichiarazione quando espressa da una dichiarazione di espressione.
Idroper

La risposta al momento è ormai superata, a partire dal 2012, i browser moderni usano un mark / sweep algorthm .. quindi non dipende più dall'ambito. Riferimenti: developer.mozilla.org/en-US/docs/Web/JavaScript/…
sksallaj

52

Fai attenzione ai riferimenti circolari quando sono coinvolti oggetti DOM:

Schemi di perdita di memoria in JavaScript

Tieni presente che la memoria può essere recuperata solo quando non ci sono riferimenti attivi all'oggetto. Questa è una trappola comune con chiusure e gestori di eventi, in quanto alcuni motori JS non verificheranno a quali variabili effettivamente si fa riferimento nelle funzioni interne e manterranno semplicemente tutte le variabili locali delle funzioni che lo racchiudono.

Ecco un semplice esempio:

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

Un'implementazione ingenua di JS non può essere raccolta bigStringfintanto che il gestore dell'evento è presente. Esistono diversi modi per risolvere questo problema, ad esempio l'impostazione bigString = nullalla fine di init()( deletenon funzionerà per le variabili locali e gli argomenti delle funzioni: deleterimuove le proprietà dagli oggetti e l'oggetto variabile è inaccessibile - ES5 in modalità rigorosa lancerà anche un ReferenceErrorse provi per cancellare una variabile locale!).

Consiglio di evitare il più possibile chiusure inutili se ti interessa il consumo di memoria.


20
Il bug di riferimento circolare DOM è specifico di JScript: nessun altro browser ne soffre se non IE. In effetti sono abbastanza sicuro che la specifica ECMAScript afferma esplicitamente che il GC deve essere in grado di gestire tali cicli: - /
olliej

@olliej: non vedo alcuna menzione del GC nelle specifiche ECMAScript .
Janus Troelsen,


16

Buona citazione tratta da un blog

Il componente DOM è "garbage collection", così come il componente JScript, il che significa che se si crea un oggetto all'interno di uno dei componenti e si perde traccia di quell'oggetto, alla fine verrà pulito.

Per esempio:

function makeABigObject() {
var bigArray = new Array(20000);
}

Quando si chiama quella funzione, il componente JScript crea un oggetto (chiamato bigArray) accessibile all'interno della funzione. Non appena la funzione ritorna, però, "perdi traccia" di bigArray perché non c'è più modo di farvi riferimento. Bene, il componente JScript si rende conto che ne hai perso la traccia, e così bigArray viene ripulito: la sua memoria viene recuperata. Lo stesso tipo di cose funziona nel componente DOM. Se dici document.createElement('div'), o qualcosa di simile, il componente DOM crea un oggetto per te. Una volta che hai perso traccia di quell'oggetto in qualche modo, il componente DOM pulirà il relativo.


13

Per quanto ne so, gli oggetti JavaScript vengono periodicamente raccolti in modo inutile quando non ci sono riferimenti all'oggetto. È qualcosa che accade automaticamente, ma se vuoi vedere di più su come funziona, a livello C ++, ha senso dare un'occhiata al codice sorgente WebKit o V8

In genere non è necessario pensarci, tuttavia, nei browser più vecchi, come IE 5.5 e le prime versioni di IE 6, e forse le versioni attuali, le chiusure creerebbero riferimenti circolari che se non controllati finirebbero per consumare memoria. Nel caso particolare che intendo per le chiusure, è stato quando hai aggiunto un riferimento JavaScript a un oggetto dom e un oggetto a un oggetto DOM che faceva riferimento all'oggetto JavaScript. Fondamentalmente non potrebbe mai essere raccolto e alla fine farebbe diventare instabile il sistema operativo nelle app di test che creavano loop per creare arresti anomali. In pratica, queste perdite sono generalmente piccole, ma per mantenere pulito il codice è necessario eliminare il riferimento JavaScript all'oggetto DOM.

Di solito è una buona idea usare la parola chiave delete per de-referenziare immediatamente oggetti di grandi dimensioni come i dati JSON che hai ricevuto e fatto tutto ciò che ti serve, specialmente nello sviluppo web mobile. Questo fa sì che il prossimo sweep del GC rimuova quell'oggetto e liberi la sua memoria.


Il problema di riferimento circolare JavaScript -> DOM -> JavaScript è stato risolto nelle versioni più recenti di IE? Se è così, da quando? Ho pensato che fosse molto profondo dal punto di vista architettonico e che probabilmente non si sarebbe mai risolto. Hai qualche fonte?
erikkallen,

Solo aneddoticamente. Non ho notato le perdite folli in IE 8 in esecuzione in modalità standard, non in modalità interrotta. Aggiusterò la mia risposta.
Heat Miser,

1
@erikkallen: sì, il bug GC è stato corretto nelle versioni di IE 8+, poiché i più vecchi utilizzavano un algoritmo di garbage collection molto ingenuo, che rendeva impossibile GC una coppia di oggetti che si riferivano l'un l'altro. Gli mark-and-sweepalgoritmi di stile più recenti si occupano di questo .
Kumarharsh,

6

garbage collection (GC) è una forma di gestione automatica della memoria rimuovendo gli oggetti che non sono più necessari.

qualsiasi processo relativo alla memoria segua questi passaggi:

1 - alloca lo spazio di memoria che ti serve

2 - eseguire alcune elaborazioni

3 - libera questo spazio di memoria

ci sono due algoritmi principali utilizzati per rilevare quali oggetti non sono più necessari.

Garbage collection conteggio dei riferimenti : questo algoritmo riduce la definizione di "un oggetto non è più necessario" a "un oggetto non ha altri oggetti che fanno riferimento ad esso", l'oggetto verrà rimosso se nessun punto di riferimento ad esso

Algoritmo Mark-and-sweep : collega ogni oggetto alla sorgente principale. qualsiasi oggetto non si connette alla radice o ad altri oggetti. questo oggetto verrà rimosso.

attualmente i browser più moderni utilizzano il secondo algoritmo.


1
E per aggiungere una fonte di questo, vedi MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/…
Xenos

4

"Nell'informatica, la garbage collection (GC) è una forma di gestione automatica della memoria. Il garbage collector, o solo il collector, tenta di recuperare immondizia, o memoria utilizzata da oggetti a cui non accederà o muterà mai più l'applicazione".

Tutti i motori JavaScript hanno i propri garbage collector e possono differire. La maggior parte delle volte non devi occuparti di loro perché fanno semplicemente quello che dovrebbero fare.

Scrivere un codice migliore dipende principalmente da quanto conosci i principi di programmazione, il linguaggio e l'implementazione particolare.


1

Che cos'è la garbage collection JavaScript?

controlla questo

Cosa è importante che un programmatore web capisca sulla raccolta dei rifiuti di JavaScript, al fine di scrivere codice migliore?

In Javascript non ti interessa l'allocazione e la deallocazione della memoria. L'intero problema è richiesto all'interprete Javascript. Le perdite sono ancora possibili in Javascript, ma sono bug dell'interprete. Se sei interessato a questo argomento, puoi leggere di più in www.memorymanagement.org


Quale dei vari sistemi di gestione della memoria nell'articolo a cui ti colleghi è quello utilizzato da JavaScript? "Le perdite sono ancora possibili in Javascript, ma sono bug dell'interprete." - Ciò non significa che i programmatori JS possano semplicemente ignorare l'intero problema, ad esempio c'è un noto problema di riferimento circolare JS <-> DOM nelle versioni precedenti di IE che potresti aggirare nel tuo codice JS. Inoltre, il modo in cui le chiusure lavoro JS è una caratteristica del progetto, non è un bug, ma è possibile legare blocchi più grandi della memoria del previsto se si utilizza chiusure "impropriamente" (sto non dicendo di non utilizzare 'em).
nnnnnn,

3
Le perdite di memoria sono un vantaggio in JavaScript. Se stai scrivendo una semplice applicazione "progetto universitario", non preoccuparti. Ma quando inizi a scrivere app di livello enterprise ad alte prestazioni, la gestione della memoria in JavaScript è un must.
Doug,

1

Su Windows puoi usare Drip.exe per trovare perdite di memoria o verificare se la tua routine di mem libera funziona.

È davvero semplice, basta inserire un URL del sito Web e vedrai il consumo di memoria del renderer IE integrato. Quindi premi aggiorna, se la memoria aumenta, hai trovato una perdita di memoria da qualche parte sulla pagina web. Ma questo è anche molto utile per vedere se le routine per liberare memoria funzionano per IE.


1

I tipi di riferimento non memorizzano l'oggetto direttamente nella variabile a cui è assegnato, quindi la variabile oggetto in questo esempio in realtà non contiene l'istanza dell'oggetto. Al contrario, contiene un puntatore (o riferimento) alla posizione in memoria in cui esiste l'oggetto

var object = new Object();

se si assegna una variabile a un'altra, ogni variabile ottiene una copia del puntatore ed entrambi fanno ancora riferimento allo stesso oggetto in memoria.

var object1 = new Object();
var object2 = object1;

Due variabili che puntano a un oggetto

JavaScript è un linguaggio spazzatura , quindi non è necessario preoccuparsi delle allocazioni di memoria quando si utilizzano i tipi di riferimento. Tuttavia, è meglio dereference oggetti che non è più necessario in modo che il garbage collector può liberare quella memoria. Il modo migliore per farlo è impostare la variabile oggetto su null.

var object1 = new Object();
// do something
object1 = null; // dereference

Dereferenziare oggetti è particolarmente importante in applicazioni molto grandi che utilizzano milioni di oggetti.

da I principi di JavaScript orientato agli oggetti - NICHOLAS C. ZAKAS

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.