2013-05-08 15 views
18
function(foo, cb) { 
    var bigObject = new BigObject(); 
    doFoo(foo, function(e) { 
    if (e.type === bigObject.type) { 
      cb(); 
      // bigObject = null; 
    } 
    }); 
} 

L'esempio precedente mostra una chiusura classica, accidentale (o forse) che perde memoria. Il garbage collector V8 non è in grado di determinare se è possibile rimuovere lo bigObject perché è in uso nella funzione di callback che può essere richiamata più volte.Perdita di memoria di chiusura e callback in javascript

Una soluzione è impostare bigObject su null quando il lavoro nella funzione di richiamata è terminato. Ma se stai usando molte variabili (immagina che ci siano n variabili come bigObject e che siano tutte utilizzate in callback), la pulizia di questo diventa un brutto problema.

La mia domanda è questa: c'è qualche altro modo per pulire quelle variabili utilizzate?

EDIT Ecco un altro esempio (reale): Così ottengo l'applicazione da mongodb e la confronta con qualche altra applicazione. La richiamata da mongodb utilizza un'applicazione variabile definita da tale callback. Dopo aver ottenuto il risultato da mongodb, lo restituisco anche come callback (perché è tutto asincrono e non posso scrivere solo return). Quindi, in realtà può accadere che i propago richiamata tutta la strada alla fonte ...

function compareApplications(application, condition, callback) { 

    var model = database.getModel('Application'); 
    model.find(condition, function (err, applicationFromMongo) { 
     var result = (applicationFromMongo.applicationID == application.applicationID) 
     callback(result)   
    } 
} 
+0

Lasciate che vi chieda questo - Perché questo è un problema ? Il gestore 'change' è pensato per essere chiamato più volte. Quindi, come mai tu (o il GC) saprai mai quando è veramente la fine dell'uso di 'bigObject' a meno che tu non disfaccia l'evento' change'? Sembra che vogliate un'istanza di 'bigObject' in modo che il gestore possa confrontare i tipi. La si istanzia una sola volta, riducendo il carico per ogni volta che viene eseguito il gestore. Se vuoi che venga ripulito, instanziarlo sempre all'interno del gestore, o aspettarti di "perdere" la memoria perché è così che funziona. – Ian

+0

Che ne dici di usare .one() al posto di .on()? – frenchie

+0

Si prega di notare che ho cambiato l'esempio. Nel mio programma del mondo reale non uso .on. Trasmetto la funzione di callback a un'altra. –

risposta

1

Se la funzione di callback è solo dovrebbe essere chiamata una volta, allora si dovrebbe cancellarsi dopo che è stato chiamato. Questo rilascerà la tua richiamata + chiusura al GC. Con la chiusura rilasciata, bigObject sarà anche libera di essere ritirata dal GC.

Questa è la soluzione migliore: come hai notato, il GC non riconosce magicamente che la tua richiamata verrà richiamata una sola volta.

+2

Grazie per aver risposto ma puoi dare qualche esempio di annullamento dell'iscrizione dal callback? –

+1

Dipende dal meccanismo utilizzato per sottoscrivere il callback. Non fornisci abbastanza dettagli su cosa 'doFoo()' fa. La maggior parte dei framework fornisce un metodo 'undoFoo()', o 'doFoo()' stesso restituisce un metodo che puoi chiamare per annullare l'iscrizione. Se stavi usando jQuery, ti iscriveresti con '$ (" ... "). On (" cambia ", cb);" e annulla l'iscrizione con '$ (" ... "). Off (" cambia ", cb) '. Infatti, hanno un metodo per la situazione esatta in cui si desidera sottoscrivere un solo evento: '$ (" ... "). Uno (" change ", cb)' che annulla automaticamente l'iscrizione dopo una singola chiamata. – Brandon

+1

Si noti che ho modificato l'esempio nel primo post. Questo è più simile al mio vero problema. Trasmetto la funzione di callback ad un'altra funzione. Io uso node.js. Foo è solo un esempio, ma il problema è lo stesso delle mie funzioni del mondo reale. La funzione di callback utilizza variabili nell'ambito e GC non può pulirlo implicitamente :(... la mia memoria rss sale molto velocemente perché ho un sacco di traffico –

0

Per costruire sulla risposta di Brandon: Se (per qualche terribile ragione) si è in grado di annullare l'iscrizione il callback si può sempre gestire l'eliminazione della richiamata da soli:

function createSingleUseCallback(callback) 
{ 
    function callbackWrapper() 
    { 
     var ret = callback.apply(this, arguments); 
     delete callback; 
     return ret; 
    } 
    return callbackWrapper; 
} 

function compareApplications(application, condition, callback) 
{ 
    var model = database.getModel('Application'); 
    model.find(condition, createSingleUseCallback(function (err, applicationFromMongo) 
    { 
     var result = (applicationFromMongo.applicationID == application.applicationID); 
     callback(result); 
    }) 
}