2013-04-07 14 views
6

Ho una perdita di memoria che non capisco. Ho programmato un meccanismo per gestire l'evento con lo slegamento semiautomatico che dovrebbe permettermi di pulire facilmente la memoria. Ma in un caso, la pulizia non avviene (utilizzo il profilo di Chrome (heap di memoria) "per verificare la presenza di istanze di" EventHandler "a sinistra). Non capisco davvero perché succede. C'è qualcosa di strano con la chiusura ...Chiusura Javascript: Perdita di memoria

see it in action with chrome

function Bind(obj, f) { 
    return function() { 
     return f.apply(obj, arguments); 
    } 
} 

function EventHandler() { 
    this.listeners = new Object(); 

    var _listenerID = 0; 
    this.addListener = function(e, obj, listener, specialDisplay) { 
     if (typeof(listener) === "function") { 
      var listenerID = ++_listenerID; 
      console.log("Events (" + (++EventHandler.All) + ", " + listenerID + ") ++" + e); 

      if (!this.listeners.hasOwnProperty(e)) { 
       this.listeners[e] = new Object(); 
      } 
      this.listeners[e][listenerID] = listener; 

      if (obj != null && typeof(obj.removeListener) == "function") { 
       var deleteListenerID = obj.addListener("Delete", null, Bind(this, function() { 
        this.removeListener(e, listenerID); 
        obj.removeListener("Delete", deleteListenerID); 
       })); 
      } 

      return listenerID; 
     } 

     return null; 
    } 
    this.fire = function(e, obj) { 
     if (this.listeners.hasOwnProperty(e)) { 
      for(var i in this.listeners[e]) { 
       this.listeners[e][i](obj); 
      } 
     } 
    } 
    this.removeListener = function(e, listenerID) { 
     if (this.listeners.hasOwnProperty(e) && this.listeners[e].hasOwnProperty(listenerID)) { 
      delete this.listeners[e][listenerID]; 

      console.log("Events (" + (--EventHandler.All) + ", " + listenerID + ") --" + e); 
     } 
    } 
} 

EventHandler.All = 0; 

function Loader() { 
} 

Loader.files = new Object(); 

Loader.LoadImage = function(src, f) { 
    if (!Loader.files.hasOwnProperty(src)) { 
     var handler = new EventHandler(); 

     console.log("Loading.... (" + src + ")"); 

     Loader.files[src] = function(fnct) { 
      handler.addListener("ImageLoaded", handler, function(img) { 
       fnct(img); 
      }); 
     } 

     handler.addListener("ImageLoaded", handler, function() { 
      Loader.files[src] = function(fnct) { 
       fnct(img); 
      } 
     });  

     var img = new Image(); 
     $(img).load(function() { 
      console.log("Loaded.... (" + src + ")"); 
      handler.fire("ImageLoaded", img); 
      handler.fire("Delete"); 
      $(img).unbind('load'); 
     }); 
     img.src = src; 
    } 

    Loader.files[src](f); 
} 

Loader.LoadImage("http://serge.snakeman.be/Demo/house.jpg", function() { alert("ok"); }); 
+0

Si prega di fare come dice il messaggio di errore e incollare il codice da jsfiddle alla domanda. Grazie. – JJJ

+0

Non so davvero quale parte del codice sarebbe tanto più significativa ero un po 'sconcertato dall'avvertimento StackOverflow. – Serge

+0

Se qualcuno potesse invocare la mia domanda, concederò una taglia 50 per la risposta ... – Serge

risposta

2

Si crea chiusure che contengono un riferimento a un EventHandler esempio tramite la variabile handler. Una delle chiusure rimane dopo che l'immagine è stata caricata:

handler.addListener("ImageLoaded", handler, function() { 
     Loader.files[src] = function(fnct) { 
      fnct(img); 
     } 
    });  

È la funzione interna function(fnct) {.... L'istanza di EventHandler non può essere liberata finché esiste la chiusura. La tua unica soluzione è liberarti di quella chiusura. Oppure si libera l'istanza manualmente, se possibile. Quanto segue potrebbe funzionare per voi:

handler.fire("Delete"); 
handler = undefined; 

profiler di memoria di Chrome mostra albero mantenendo dell'oggetto, che è solo un altro modo per dire "Chi è in possesso di un riferimento a tale oggetto". Nel tuo esempio è EventHandler < - handler (la variabile del metodo LoadImage come incorporata dalla chiusura) < - house.jpg, che in realtà è Loader.files[src] e ha il valore function(fnct) { fnct(img); }.

+0

Quando l'immagine viene caricata, la riserva di riferimento in Caricatore.i file [src] vengono eliminati poiché l'ho impostato su un altro valore. Loader.files [src] = function (fnct) { fnct (img); } – Serge

+0

Hai ragione, certo, mi sono perso. Ma questo non cambia molto: questa funzione impedisce il rilascio dell'istanza di EventHandler (come dice il profiler). Non sono sicuro al 100%, ma a patto che esista una funzione, che possa fare riferimento alla variabile del gestore, l'istanza EventHandler non verrà rilasciata. – zeroflagL

+0

Cosa suggerisci per la funzione di non esistere più? Non capisco dove potrebbe essere in ogni caso. : S – Serge

2

Mentre si aggiunge "ascoltatori", assicurarsi di rimuoverlo, se si utilizza la query per un lungo periodo di tempo.

this.listeners = new Object(); 

o

this.listeners[e] = new Object(); 

questo aggiungerà oggetto ascoltatore matrice, ma non rimuoverli in qualsiasi punto.

Questa potrebbe essere la causa del consumo di memoria. Potrebbe non perdere, la sua assegnazione di oggetti. che consuma la tua RAM usando il browser. :)

+0

Li sto rimuovendo. Sulla console è possibile vedere "Eventi (0, 4) - Elimina" che indicano che l'ultimo listener rimanente è stato rimosso. – Serge

+0

La sua pratica generale quando si ha a che fare con gli ascoltatori nel sistema. È necessario gestirli, inclusa la loro rimozione. Lo stesso vale per i browser in alcuni casi :) Se li rimuovi, è fuori dalla mia portata. ma per un doppio controllo, controlla l'elemento nella console 'ascoltatori []'. Se ha elementi o no. – MarmiK

+0

L'aggiunta di oggetti alla console rende Chrome un altro riferimento su di essi. Ma dal momento che stai solo parlando degli ascoltatori [] non dovresti ferirti provandoci (ci proverò stasera). – Serge