Ich habe eine ziemlich komplexe Javascript-App, die eine Hauptschleife hat, die 60 Mal pro Sekunde aufgerufen wird. Es scheint eine Menge Speicherbereinigung im Gange zu sein (basierend auf der Sägezahnausgabe von der Speicherzeitleiste in den Chrome-Entwicklungstools) - und dies wirkt sich häufig auf die Leistung der Anwendung aus.
Daher versuche ich, nach Best Practices zu suchen, um den Arbeitsaufwand für den Garbage Collector zu reduzieren. (Die meisten Informationen, die ich im Internet finden konnte, beziehen sich auf die Vermeidung von Speicherlecks. Dies ist eine etwas andere Frage. Mein Speicher wird freigegeben, es wird nur zu viel Speicherplatz gesammelt.) Ich gehe davon aus dass es hauptsächlich darauf ankommt, Objekte so oft wie möglich wiederzuverwenden, aber natürlich steckt der Teufel im Detail.
Die App ist in 'Klassen' nach dem Vorbild von John Resigs Simple JavaScript Inheritance strukturiert .
Ich denke, ein Problem ist, dass einige Funktionen tausende Male pro Sekunde aufgerufen werden können (da sie bei jeder Iteration der Hauptschleife hunderte Male verwendet werden), und möglicherweise die lokalen Arbeitsvariablen in diesen Funktionen (Strings, Arrays usw.). könnte das Problem sein.
Ich bin mir des Objekt-Poolings für größere / schwerere Objekte bewusst (und wir verwenden dies bis zu einem gewissen Grad), aber ich suche nach Techniken, die auf der ganzen Linie angewendet werden können, insbesondere in Bezug auf Funktionen, die in engen Schleifen sehr oft aufgerufen werden .
Mit welchen Techniken kann ich den Arbeitsaufwand für den Garbage Collector reduzieren?
Und vielleicht auch - mit welchen Techniken kann ermittelt werden, welche Objekte am häufigsten im Müll gesammelt werden? (Es ist eine sehr große Codebasis, daher war der Vergleich von Schnappschüssen des Heaps nicht sehr fruchtbar.)
quelle
Antworten:
Viele der Dinge, die Sie tun müssen, um die GC-Abwanderung zu minimieren, widersprechen dem, was in den meisten anderen Szenarien als idiomatisch angesehen wird. Denken Sie also bitte an den Kontext, wenn Sie die Ratschläge beurteilen, die ich gebe.
Die Zuordnung erfolgt bei modernen Dolmetschern an mehreren Stellen:
new
oder über die Literal-Syntax erstellen[...]
, oder{}
.(function (...) { ... })
.Object(myNumber)
oder erzwingtNumber.prototype.toString.call(42)
Array.prototype.slice
.arguments
, um über die Parameterliste zu reflektieren.Vermeiden Sie dies und bündeln und verwenden Sie Objekte nach Möglichkeit.
Achten Sie insbesondere auf folgende Möglichkeiten:
split
Übereinstimmungen oder Übereinstimmungen mit regulären Ausdrücken, da für jede mehrere Objektzuweisungen erforderlich sind. Dies geschieht häufig mit Schlüsseln in Nachschlagetabellen und dynamischen DOM-Knoten-IDs. Zum BeispiellookupTable['foo-' + x]
unddocument.getElementById('foo-' + x)
beide beinhalten eine Zuordnung, da es eine Zeichenfolgenverkettung gibt. Oft können Sie Schlüssel an langlebige Objekte anhängen, anstatt sie erneut zu verketten. Abhängig von den Browsern, die Sie unterstützen müssen, können Sie diese möglicherweise verwendenMap
Objekte direkt als Schlüssel verwenden.try { op(x) } catch (e) { ... }
tunif (!opCouldFailOn(x)) { op(x); } else { ... }
.JSON.stringify
die einen internen nativen Puffer verwendet, um Inhalte zu akkumulieren, anstatt mehrere Objekte .arguments
Funktionen, die beim Aufruf ein Array-ähnliches Objekt erstellen müssen.Ich schlug
JSON.stringify
vor, ausgehende Netzwerknachrichten zu erstellen. Das Parsen von Eingabenachrichten mit verwendetJSON.parse
natürlich eine Zuordnung, und viele davon für große Nachrichten. Wenn Sie Ihre eingehenden Nachrichten als Arrays von Grundelementen darstellen können, können Sie viele Zuordnungen speichern. Die einzige andere integrierte Funktion, um die Sie einen Parser erstellen können, der nicht zugeordnet ist, istString.prototype.charCodeAt
. Ein Parser für ein komplexes Format, das nur das verwendet, wird allerdings höllisch zu lesen sein.quelle
JSON.parse
d-Objekte weniger (oder gleichen) Speicherplatz zuweisen als die Nachrichtenzeichenfolge?Die Chrome-Entwicklertools bieten eine sehr schöne Funktion zum Nachverfolgen der Speicherzuordnung. Es heißt Memory Timeline. Dieser Artikel beschreibt einige Details. Ich nehme an, das ist es, wovon du sprichst, was den "Sägezahn" betrifft? Dies ist für die meisten GC-Laufzeiten normal. Die Zuordnung wird fortgesetzt, bis ein Nutzungsschwellenwert erreicht ist, der eine Sammlung auslöst. Normalerweise gibt es verschiedene Arten von Sammlungen mit unterschiedlichen Schwellenwerten.
Garbage Collections werden zusammen mit ihrer Dauer in die dem Trace zugeordnete Ereignisliste aufgenommen. Auf meinem ziemlich alten Notizbuch treten kurzlebige Sammlungen bei etwa 4 MB auf und dauern 30 ms. Dies sind 2 Ihrer 60-Hz-Schleifeniterationen. Wenn es sich um eine Animation handelt, verursachen 30-ms-Sammlungen wahrscheinlich Stottern. Sie sollten hier beginnen, um zu sehen, was in Ihrer Umgebung vor sich geht: Wo liegt der Sammlungsschwellenwert und wie lange Ihre Sammlungen dauern. Dies gibt Ihnen einen Bezugspunkt zur Bewertung von Optimierungen. Aber Sie werden wahrscheinlich nichts Besseres tun, als die Häufigkeit des Stotterns zu verringern, indem Sie die Zuordnungsrate verlangsamen und das Intervall zwischen den Sammlungen verlängern.
Der nächste Schritt ist die Verwendung der Profile | Funktion "Datensatz-Heap-Zuordnungen" zum Generieren eines Katalogs von Zuordnungen nach Datensatztyp. Dadurch wird schnell angezeigt, welche Objekttypen während des Ablaufzeitraums den meisten Speicher belegen, was der Zuordnungsrate entspricht. Konzentrieren Sie sich in absteigender Reihenfolge auf diese.
Die Techniken sind keine Raketenwissenschaft. Vermeiden Sie Objekte in Boxen, wenn Sie mit Objekten in Boxen arbeiten können. Verwenden Sie globale Variablen, um Objekte mit einzelnen Boxen zu speichern und wiederzuverwenden, anstatt in jeder Iteration neue zuzuweisen. Bündeln Sie allgemeine Objekttypen in freien Listen, anstatt sie aufzugeben. Ergebnisse der Verkettung von Cache-Zeichenfolgen, die wahrscheinlich in zukünftigen Iterationen wiederverwendet werden können. Vermeiden Sie die Zuordnung, um nur Funktionsergebnisse zurückzugeben, indem Sie stattdessen Variablen in einem umschließenden Bereich festlegen. Sie müssen jeden Objekttyp in einem eigenen Kontext betrachten, um die beste Strategie zu finden. Wenn Sie Hilfe bei Einzelheiten benötigen, veröffentlichen Sie eine Bearbeitung, in der Details der Herausforderung beschrieben werden, die Sie sich ansehen.
Ich rate davon ab, Ihren normalen Codierungsstil während einer Anwendung zu verfälschen, wenn Sie versuchen, weniger Müll zu produzieren. Aus dem gleichen Grund sollten Sie die Geschwindigkeit nicht vorzeitig optimieren. Der größte Teil Ihrer Bemühungen sowie ein Großteil der zusätzlichen Komplexität und Unklarheit des Codes sind bedeutungslos.
quelle
request animation frame
, sind: ,,animation frame fired
undcomposite layers
. Ich habe keine Ahnung, warum ich nicht so seheGC Event
wie Sie (dies ist auf der neuesten Version von Chrom und auch Kanarienvogel).@342342
undcode relocation info
.Grundsätzlich möchten Sie so viel wie möglich zwischenspeichern und so wenig wie möglich für jeden Lauf Ihrer Schleife erstellen und zerstören.
Das erste, was mir in den Sinn kommt, ist, die Verwendung anonymer Funktionen (falls vorhanden) in Ihrer Hauptschleife zu reduzieren. Es wäre auch leicht, in die Falle zu tappen, Objekte zu erstellen und zu zerstören, die an andere Funktionen übergeben werden. Ich bin kein Javascript-Experte, aber ich würde mir vorstellen, dass dies:
würde viel schneller laufen als dies:
Gibt es jemals Ausfallzeiten für Ihr Programm? Vielleicht muss es ein oder zwei Sekunden lang reibungslos laufen (z. B. für eine Animation), und dann hat es mehr Zeit für die Verarbeitung? Wenn dies der Fall ist, könnte ich sehen, wie Objekte, die normalerweise während der gesamten Animation als Müll gesammelt werden, in einem globalen Objekt referenziert werden. Wenn die Animation endet, können Sie alle Referenzen löschen und den Garbage Collector seine Arbeit erledigen lassen.
Tut mir leid, wenn das alles etwas trivial ist im Vergleich zu dem, was Sie bereits versucht und gedacht haben.
quelle
Ich würde ein oder wenige Objekte im
global scope
(wo ich sicher bin, dass der Garbage Collector sie nicht berühren darf) erstellen und dann versuchen, meine Lösung so umzugestalten, dass diese Objekte verwendet werden, um die Arbeit zu erledigen, anstatt lokale Variablen zu verwenden .Natürlich konnte es nicht überall im Code gemacht werden, aber im Allgemeinen vermeide ich so Garbage Collector.
PS Es könnte dazu führen, dass dieser bestimmte Teil des Codes etwas weniger wartbar ist.
quelle