2015-09-30 5 views
6

sto leggendo questo articolo (http://javascript.info/tutorial/memory-leaks#memory-leak-size) sulle perdite di memoria che cita questo come una perdita di memoria:Abbiamo manualmente bisogno di pulire le variabili senza riferimento in una chiusura?

function f() { 
    var data = "Large piece of data"; 

    function inner() { 
     return "Foo"; 
    } 

    return inner; 
} 

interprete JavaScript non ha idea di quali variabili possono essere richiesti dal la funzione interna, in modo che mantiene qualunque cosa. In ogni ambiente esterno LexicalEnvironment. Spero che i nuovi interpreti cerchino di ottimizzarlo, ma lo non è sicuro del loro successo.

L'articolo suggerisce che è necessario impostare manualmente data = null prima di restituire la funzione interna.

Questo vale oggi? O questo articolo è obsoleto? (Se è obsoleto, qualcuno può indicarmi una risorsa sulle attuali insidie)

risposta

5

I motori moderni non avrebbero mantenuto le variabili inutilizzate nell'ambito esterno.

Pertanto, non importa se si imposta data = null prima di restituire la funzione interna, poiché la funzione interna non dipende da ("close over") data.

Se la funzione interna fatto dipendono data --perhaps lo ritorna in risposta - quindi impostando data = null è certamente non ciò che si vuole, perché poi, beh, sarebbe nullo invece di avere il suo valore originale !

Assumendo che la funzione interna non dipendere data, allora sì, purché inner si punta (previste) qualcosa, allora il valore di data dovrà essere mantenuta intorno. Ma è quello che stai dicendo che vuoi! Come puoi avere qualcosa a disposizione senza che sia disponibile?

Ricordare che a un certo punto la variabile che contiene il valore di ritorno di f() non sarà più disponibile. A questo punto, almeno fino al momento in cui viene chiamato di nuovo f(), il valore data verrà raccolto automaticamente.

La regola generale è che non è necessario preoccuparsi della memoria e perdite con JavaScript. Questo è il punto centrale di GC. Il garbage collector fa un ottimo lavoro nell'individuare ciò che è necessario e ciò che non è necessario, e mantenere il primo e l'immondizia nel raccogliere quest'ultimo.

Si può prendere in considerazione il seguente esempio:

function foo() { 
    var x = 1; 
    return function() { debugger; return 1; }; 
} 

function bar() { 
    var x = 1; 
    return function() { debugger; return x; }; 
} 

foo()(); 
bar()(); 

ed esaminare la sua esecuzione in Chrome DevTools finestra variabile. Quando il debugger si arresta nella funzione interna di foo, notare che x non è presente come variabile locale o come chiusura. Per tutti gli scopi pratici, non esiste.

Quando il debugger si arresta nella funzione interna di bar, vediamo la variabile x, perché doveva essere conservata in modo da essere accessibile per poter essere restituita.

Ciò vale oggi? O questo articolo è obsoleto?

No, non lo è, e sì, lo è. L'articolo ha quattro anni, che è una vita nel mondo del web. Non ho modo di sapere se jQuery è ancora soggetto a perdite, ma sarei sorpreso se lo fosse, e se è così, c'è un modo abbastanza semplice per evitarli - non usare jQuery. Le fughe che l'autore dell'articolo menziona relative ai loop DOM e ai gestori di eventi non sono presenti nei browser moderni, con cui intendo IE10 (più probabile IE9) e sopra. Ti suggerirei di trovare un riferimento più aggiornato se vuoi veramente capire le perdite di memoria. In realtà, ti suggerirei principalmente di smettere di preoccuparti delle perdite di memoria. Si verificano solo in situazioni molto specializzate. È difficile trovare molto sull'argomento sul web in questi giorni per quella precisa ragione. Ecco un articolo che ho trovato: http://point.davidglasser.net/2013/06/27/surprising-javascript-memory-leak.html.

+0

Grazie a torazaburo. Ciò che mi preoccupa è che l'ultimo frammento nell'articolo che hai fornito sul bug di Meteor effettivamente perde memoria in V8. – Jonathan

+0

"In realtà, ti suggerisco di smettere principalmente di preoccuparti delle perdite di memoria, si verificano solo in situazioni molto specializzate". Sono fortemente in disaccordo con questa affermazione. Almeno i DOM distaccati sono un evento frequente nelle SPA e ogni sviluppatore deve saperlo. –

1

Proprio in aggiunta all'eccellente risposta di @ torazaburo, vale la pena sottolineare che gli esempi in questo tutorial non sono fughe di notizie. Una perdita che accade quando un programma cancella un riferimento a qualcosa ma non rilascia la memoria che consuma.

L'ultima volta che ricordo che gli sviluppatori JS dovevano davvero preoccuparsi di perdite reali era quando Internet Explorer (6 e 7 credo) usava gestione della memoria separata per DOM e per JS. Per questo motivo, è stato possibile associare un evento onclick a un pulsante, distruggere il pulsante e mantenere il gestore eventi inutilizzato in memoria, per sempre (o fino a quando il browser non si è arrestato o è stato chiuso dall'utente). Non è possibile attivare il gestore o rilasciarlo dopo il fatto. Si è appena seduto nello stack, occupando spazio. Quindi, se hai una webapp di lunga durata o una pagina web che ha creato e distrutto molti elementi DOM, devi essere super diligente per separare gli eventi prima di distruggerli.

Ho anche avuto alcune fastidiose perdite in iOS, ma questi erano tutti bug e sono stati (eventualmente) rattoppati da Apple.

Detto questo, un buon sviluppatore deve tenere a mente la gestione delle risorse durante la scrittura del codice. Considerate queste due costruttori:

function F() { 
    var data = "One megabyte of data"; 

    this.inner = new function() { 
     return data; 
    } 
} 

var G = function() {}; 
G.prototype.data = "One megabyte of data"; 
G.prototype.inner = function() { 
    return this.data; 
}; 

Se si dovesse creare un migliaio di casi di F, il browser avrebbe dovuto assegnare un gigabyte extra di memoria per tutte quelle copie della stringa enorme. E ogni volta che hai cancellato un'istanza, potresti ottenere un po 'di frivolezza sullo schermo quando il GC alla fine ha recuperato quella ram. D'altra parte, se hai fatto mille istanze di G, la stringa enorme sarebbe stata creata una volta e riutilizzata da ogni istanza. Questo è un enorme incremento delle prestazioni.

Ma il vantaggio di F è che la stringa enorme è essenzialmente privata. Nessun altro codice al di fuori del costruttore sarebbe in grado di accedere direttamente a quella stringa. Per questo motivo, ogni istanza di F potrebbe modificare quella stringa quanto desidera e non dovresti mai preoccuparti di causare problemi ad altre istanze.

D'altra parte, l'enorme stringa in G è disponibile per chiunque. Altre istanze potrebbero cambiarlo e qualsiasi codice che condivide lo stesso ambito di G potrebbe anche.

Quindi, in questo caso, c'è un compromesso tra uso delle risorse e sicurezza.

+0

Bene menzionare l'aggiunta di dati condivisi al prototipo.Tralascio anche un collegamento qui a [questa domanda sui riferimenti circolari in js] (http://stackoverflow.com/q/7347203/2407212) in quanto ciò si riferisce anche a credenze comuni riguardanti le perdite di memoria. – Jonathan