2013-04-07 14 views
6

Ich habe ein Speicherleck, das ich nicht verstehe. Ich programmierte einen Mechanismus, um das Ereignis mit halbautomatischer Entkopplung zu behandeln, was mir erlauben sollte, den Speicher leicht zu bereinigen. Aber in einem Fall passiert das Aufräumen nicht (ich verwende chroms "Profil (Speicher-Heap)", um nach Instanzen von "EventHandler" zu suchen). Ich verstehe wirklich nicht, warum es passiert. Es ist etwas seltsam mit der Schließung ...Javascript schließen: Speicherleck

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

Könnten Sie bitte tun, wie die Fehlermeldung sagt und fügen Sie den Code aus jsfiddle zu der Frage. Vielen Dank. – JJJ

+0

Ich weiß wirklich nicht, welcher Teil des Codes um so aussagekräftiger wäre, als ich tatsächlich von der Stackoverflow-Warnung etwas beunruhigt war. – Serge

+0

Wenn jemand meine Frage verbessern könnte, gebe ich 50 Bounty für die Antwort ... – Serge

Antwort

2

Sie Verschlüsse erstellen, die einen Verweis auf eine EventHandler Instanz über die handler Variable halten. Einer der Verschlüsse bleibt, nachdem das Bild geladen wurde:

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

Es ist die innere Funktion function(fnct) {.... Die Instanz EventHandler kann nicht freigegeben werden, solange der Abschluss vorhanden ist. Ihre einzige Lösung ist, diese Schließung loszuwerden. Oder Sie befreien die Instanz nach Möglichkeit manuell. Die folgende könnte für Sie arbeiten:

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

Chrome Speicher-Profiler zeigt Ihnen die Haltebaum des Objekts, die nur eine andere Art zu sagen, „Wer hat das Objekt einen Verweis hält“. In Ihrem Beispiel ist es Eventhandler < - Handler (die Variable der Loadimage-Methode, wie durch den Verschluss eingebaut) < - house.jpg, die eigentlich Loader.files[src] ist und haben den Wert function(fnct) { fnct(img); }.

+0

Wenn das Bild geladen wird, wird die Referenz in Loader geladen.Dateien [src] werden gelöscht, da ich sie auf einen anderen Wert gesetzt habe. Loader.files [src] = Funktion (fnct) { fnct (img); } – Serge

+0

Sie haben Recht, natürlich habe ich das vermisst. Aber das ändert nicht viel: Diese Funktion verhindert, dass die EventHandler-Instanz freigegeben wird (sagt der Profiler). Ich bin nicht 100% sicher, aber afaik solange eine Funktion existiert, die auf die Handler-Variable verweisen kann, wird die EventHandler-Instanz nicht freigegeben. – zeroflagL

+0

Was ist der Grund dafür, dass die Funktion nicht mehr existiert? Ich verstehe nicht, wo es gehalten werden könnte. : S – Serge

2

Während Sie Listener hinzufügen, stellen Sie sicher, dass Sie sie entfernen, wenn Sie die Abfrage lange verwenden.

this.listeners = new Object(); 

oder

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

dieses Objekt Zuhörers als Array hinzuzufügen, aber nicht, sie an einer beliebigen Stelle entfernt wird.

Dies könnte der Grund für den Speicherverbrauch sein. Es darf nicht leak, seine Zuordnung von Objekten. das verbraucht Ihr RAM mit Browser. :)

+0

Ich entferne sie. Auf der Konsole können Sie "Events (0, 4) --Delete" sehen, die anzeigen, dass der letzte verbleibende Listener entfernt wurde. – Serge

+0

Seine allgemeine Praxis, wenn Sie mit Zuhörern im System zu tun haben. Sie müssen sie behandeln und sie entfernen. Das gleiche gilt für den Browser in einigen Fällen :) Wenn Sie sie entfernen, dann ist es außerhalb meiner Reichweite. überprüfen Sie das Element in der Konsole 'listeners []'. Ob es Elemente hat oder nicht. – MarmiK

+0

Wenn Sie der Konsole ein Objekt hinzufügen, hält Chrome eine weitere Referenz auf ihnen. Aber da du gerade über die Zuhörer sprichst [] sollte es nicht weh tun (versuch ich es heute Abend). – Serge