Ich habe einen sehr einfachen Testfall erstellt, der eine Backbone-Ansicht erstellt, einen Handler an ein Ereignis anfügt und eine benutzerdefinierte Klasse instanziiert. Ich glaube, dass durch Klicken auf die Schaltfläche "Entfernen" in diesem Beispiel alles bereinigt wird und keine Speicherlecks auftreten sollten.
Eine jsfiddle für den Code finden Sie hier: http://jsfiddle.net/4QhR2/
// scope everything to a function
function main() {
function MyWrapper() {
this.element = null;
}
MyWrapper.prototype.set = function(elem) {
this.element = elem;
}
MyWrapper.prototype.get = function() {
return this.element;
}
var MyView = Backbone.View.extend({
tagName : "div",
id : "view",
events : {
"click #button" : "onButton",
},
initialize : function(options) {
// done for demo purposes only, should be using templates
this.html_text = "<input type='text' id='textbox' /><button id='button'>Remove</button>";
this.listenTo(this,"all",function(){console.log("Event: "+arguments[0]);});
},
render : function() {
this.$el.html(this.html_text);
this.wrapper = new MyWrapper();
this.wrapper.set(this.$("#textbox"));
this.wrapper.get().val("placeholder");
return this;
},
onButton : function() {
// assume this gets .remove() called on subviews (if they existed)
this.trigger("cleanup");
this.remove();
}
});
var view = new MyView();
$("#content").append(view.render().el);
}
main();
Ich bin mir jedoch nicht sicher, wie ich mit dem Profiler von Google Chrome überprüfen soll, ob dies tatsächlich der Fall ist. Es gibt eine Unmenge Dinge, die im Snapshot des Heap-Profilers angezeigt werden, und ich habe keine Ahnung, wie ich entschlüsseln kann, was gut / schlecht ist. In den Tutorials, die ich bisher gesehen habe, werde ich entweder nur aufgefordert, "den Snapshot-Profiler zu verwenden" oder mir ein äußerst detailliertes Manifest zur Funktionsweise des gesamten Profilers zu geben. Ist es möglich, den Profiler nur als Werkzeug zu verwenden, oder muss ich wirklich verstehen, wie das Ganze entwickelt wurde?
BEARBEITEN: Tutorials wie diese:
Behebung von Google Mail-Speicherlecks
Sind repräsentativ für einige der stärkeren Materialien da draußen, von dem, was ich gesehen habe. Abgesehen von der Einführung des Konzepts der 3-Schnappschuss-Technik bieten sie jedoch nur sehr wenig praktisches Wissen (für einen Anfänger wie mich). Das Tutorial "Verwenden von DevTools" funktioniert nicht anhand eines realen Beispiels, daher ist die vage und allgemeine konzeptionelle Beschreibung der Dinge nicht besonders hilfreich. Wie für das Beispiel "Google Mail":
Sie haben also ein Leck gefunden. Was jetzt?
Untersuchen Sie den Haltepfad von durchgesickerten Objekten in der unteren Hälfte des Bedienfelds „Profile“
Wenn die Zuweisungsstelle nicht leicht abgeleitet werden kann (dh Ereignis-Listener):
Instrumentieren Sie den Konstruktor des Aufbewahrungsobjekts über die JS-Konsole, um die Stapelverfolgung für Zuordnungen zu speichern
Verwenden Sie Closure? Aktivieren Sie das entsprechende vorhandene Flag (z. B. goog.events.Listener.ENABLE_MONITORING), um die Eigenschaft createdStack während der Erstellung festzulegen
Nachdem ich das gelesen habe, bin ich verwirrter, nicht weniger. Und wieder sagt es mir nur, dass ich Dinge tun soll , nicht wie ich sie tun soll. Aus meiner Sicht sind alle Informationen entweder zu vage oder würden nur für jemanden Sinn machen, der den Prozess bereits verstanden hat.
Einige dieser spezifischeren Probleme wurden in der Antwort von @Jonathan Naguin unten angesprochen .
quelle
main
10.000 statt einmal aufzurufen und zu prüfen , ob am Ende viel mehr Speicher verwendet wird.Antworten:
Ein guter Workflow zum Auffinden von Speicherlecks ist die Drei-Schnappschuss- Technik, die zuerst von Loreena Lee und dem Google Mail-Team verwendet wurde, um einige ihrer Speicherprobleme zu lösen. Die Schritte sind im Allgemeinen:
Für Ihr Beispiel habe ich den Code angepasst, um diesen Prozess zu zeigen (Sie finden ihn hier ) und die Erstellung der Backbone-Ansicht bis zum Klickereignis der Schaltfläche Start verzögert. Jetzt:
Jetzt sind Sie bereit, Speicherlecks zu finden!
Sie werden Knoten in einigen verschiedenen Farben bemerken. Rote Knoten haben keine direkten Verweise von Javascript auf sie, sind aber lebendig, weil sie Teil eines getrennten DOM-Baums sind. Es kann einen Knoten in dem Baum geben, auf den aus Javascript verwiesen wird (möglicherweise als Abschluss oder Variable), der jedoch zufällig verhindert, dass der gesamte DOM-Baum durch Müll gesammelt wird.
Gelbe Knoten haben jedoch direkte Verweise aus Javascript. Suchen Sie im gleichen getrennten DOM-Baum nach gelben Knoten, um Referenzen aus Ihrem Javascript zu finden. Es sollte eine Eigenschaftskette geben, die vom DOM-Fenster zum Element führt.
In Ihrem speziellen Bereich sehen Sie ein HTML Div-Element, das als rot markiert ist. Wenn Sie das Element erweitern, sehen Sie, dass es von einer "Cache" -Funktion referenziert wird.
Wählen Sie die Zeile aus und in Ihrem Konsolentyp $ 0 sehen Sie die tatsächliche Funktion und Position:
Hier wird auf Ihr Element verwiesen. Leider können Sie nicht viel tun, es ist ein interner Mechanismus von jQuery. Gehen Sie jedoch nur zu Testzwecken in die Funktion und ändern Sie die Methode in:
Wenn Sie den Vorgang wiederholen, sehen Sie keinen roten Knoten :)
Dokumentation:
quelle
$0
Funktion in der Konsole, die für mich neu war - natürlich habe ich keine Ahnung, was das tut oder woher Sie wissen, dass Sie es verwenden ($1
scheint nutzlos zu sein, während$2
es dasselbe zu tun scheint). Zweitens, woher wussten Sie, dass Sie die Zeile#button in function cache()
und nicht eine der anderen Dutzenden von Zeilen hervorheben sollten ? Schließlich gibt es rote Knoten inNodeList
undHTMLInputElement
auch, aber ich kann sie nicht herausfinden.cache
Zeile Informationen enthielt, während die anderen dies nicht taten? Es gibt zahlreiche Zweige, die einen geringeren Abstand haben als dercache
eine. Und ich bin mir nicht sicher, woher du wusstest, dass dasHTMLInputElement
ein Kind von istHTMLDivElement
. Ich sehe, dass darin darauf verwiesen wird ("native in HTMLDivElement"), aber es verweist auch auf sich selbst und zweiHTMLButtonElement
s, was für mich keinen Sinn ergibt. Ich würde es sicherlich begrüßen, wenn Sie die Antwort für dieses Beispiel identifizieren würden, aber ich hätte wirklich keine Ahnung, wie ich dies auf andere Probleme verallgemeinern könnte.Filter objects allocated between Snapshots 1 and 2 in Snapshot 3's "Summary" view.
dasHier ist ein Tipp zur Speicherprofilerstellung einer jsfiddle: Verwenden Sie die folgende URL, um Ihr jsfiddle-Ergebnis zu isolieren. Es entfernt das gesamte jsfiddle-Framework und lädt nur Ihr Ergebnis.
http://jsfiddle.net/4QhR2/show/
Ich konnte nie herausfinden, wie ich mithilfe der Zeitleiste und des Profilers Speicherlecks aufspüren kann, bis ich die folgende Dokumentation gelesen habe. Nachdem ich den Abschnitt mit dem Titel "Objektzuordnungs-Tracker" gelesen hatte, konnte ich das Tool "Heap-Zuordnungen aufzeichnen" verwenden und einige getrennte DOM-Knoten verfolgen.
Ich habe das Problem behoben, indem ich von der jQuery-Ereignisbindung zur Verwendung der Backbone-Ereignisdelegierung gewechselt bin. Nach meinem Verständnis werden neuere Versionen von Backbone die Ereignisse automatisch aufheben, wenn Sie anrufen
View.remove()
. Führen Sie einige der Demos selbst aus. Sie sind mit Speicherlecks ausgestattet, die Sie identifizieren können. Sie können hier gerne Fragen stellen, wenn Sie diese nach dem Studium dieser Dokumentation immer noch nicht erhalten.https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling
quelle
Grundsätzlich müssen Sie sich die Anzahl der Objekte in Ihrem Heap-Snapshot ansehen. Wenn die Anzahl der Objekte zwischen zwei Schnappschüssen zunimmt und Sie Objekte entsorgt haben, liegt ein Speicherverlust vor. Mein Rat ist, in Ihrem Code nach Ereignishandlern zu suchen, die nicht getrennt werden.
quelle
Window/http://jsfiddle.net/4QhR2/show
könnte nützlich sein, aber es sind nur endlose Funktionen. Ich habe keine Ahnung, was da drin los ist.Es gibt ein Einführungsvideo von Google, das sehr hilfreich sein wird, um JavaScript-Speicherlecks zu finden.
https://www.youtube.com/watch?v=L3ugr9BJqIs
quelle
Sie können auch die Registerkarte Zeitleiste in den Entwicklertools anzeigen. Notieren Sie die Nutzung Ihrer App und behalten Sie die Anzahl der DOM-Knoten und Ereignis-Listener im Auge.
Wenn das Speicherdiagramm tatsächlich einen Speicherverlust anzeigen würde, können Sie den Profiler verwenden, um herauszufinden, was undicht ist.
quelle
Vielleicht möchten Sie auch lesen:
http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/
Es wird die Verwendung der Chrome-Entwicklertools erläutert und es werden schrittweise Anleitungen zum Bestätigen und Auffinden eines Speicherverlusts mithilfe des Heap-Snapshot-Vergleichs und der verschiedenen verfügbaren Hep-Snapshot-Ansichten gegeben.
quelle
Ich stimme dem Rat zu, einen Heap-Schnappschuss zu machen. Sie sind hervorragend zum Erkennen von Speicherlecks geeignet. Chrome leistet hervorragende Arbeit beim Schnappschuss.
In meinem Forschungsprojekt für meinen Abschluss habe ich eine interaktive Webanwendung erstellt, die viele Daten generieren musste, die in "Ebenen" aufgebaut waren. Viele dieser Ebenen wurden in der Benutzeroberfläche "gelöscht", aber aus irgendeinem Grund war der Speicher nicht vorhanden Durch die Freigabe konnte ich mithilfe des Snapshot-Tools feststellen, dass JQuery eine Referenz auf das Objekt gespeichert hatte (die Quelle war, als ich versuchte, ein
.load()
Ereignis auszulösen, bei dem die Referenz beibehalten wurde, obwohl der Gültigkeitsbereich überschritten wurde). Wenn diese Informationen im Alleingang zur Verfügung stehen und mein Projekt gespeichert ist, ist dies ein äußerst nützliches Tool, wenn Sie die Bibliotheken anderer Personen verwenden und das Problem bestehender Referenzen besteht, die den GC daran hindern, seine Arbeit zu erledigen.BEARBEITEN: Es ist auch nützlich, im Voraus zu planen, welche Aktionen Sie ausführen werden, um den Zeitaufwand für das Aufnehmen von Schnappschüssen zu minimieren, Hypothesen aufzustellen, was das Problem verursachen könnte, und jedes Szenario zu testen und vorher und nachher Schnappschüsse zu erstellen.
quelle
Einige wichtige Hinweise zum Erkennen von Speicherlecks mithilfe von Chrome Developer-Tools:
1) Chrome selbst weist Speicherlecks für bestimmte Elemente wie Kennwort- und Nummernfelder auf. https://bugs.chromium.org/p/chromium/issues/detail?id=967438 . Vermeiden Sie es, diese beim Debuggen zu verwenden, da sie Ihren Heap-Snapshot bei der Suche nach getrennten Elementen beschädigen.
2) Vermeiden Sie es, etwas in der Browserkonsole zu protokollieren . Chrome sammelt keine Objekte, die in die Konsole geschrieben wurden, und wirkt sich somit auf Ihr Ergebnis aus. Sie können die Ausgabe unterdrücken, indem Sie den folgenden Code am Anfang Ihres Skripts / Ihrer Seite einfügen:
3) Verwenden Sie Heap-Snapshots und suchen Sie nach "Trennen", um getrennte DOM-Elemente zu identifizieren. Durch Bewegen des Mauszeigers erhalten Sie Zugriff auf alle Eigenschaften, einschließlich ID und OuterHTML, mit deren Hilfe Sie jedes Element identifizieren können. Wenn die getrennten Elemente immer noch zu allgemein sind, um sie zu erkennen, weisen Sie ihnen vor dem Ausführen des Tests über die Browserkonsole eindeutige IDs zu, z.
Wenn Sie nun ein getrenntes Element mit identifizieren, sagen wir id = "AutoId_49", laden Sie Ihre Seite neu, führen Sie das obige Snippet erneut aus und suchen Sie das Element mit id = "AutoId_49" mithilfe des DOM-Inspektors oder document.querySelector (..) . Dies funktioniert natürlich nur, wenn Ihr Seiteninhalt vorhersehbar ist.
Wie ich meine Tests durchführe, um Speicherlecks zu identifizieren
1) Seite laden (mit unterdrückter Konsolenausgabe!)
2) Machen Sie Dinge auf der Seite, die zu Speicherlecks führen können
3) Verwenden Sie die Entwicklertools, um einen Heap-Snapshot zu erstellen und nach "Trennen" zu suchen.
4) Hover Elemente sie von ihrer identifizieren ID oder Outerhtml Eigenschaften
quelle