JavaScript ist ja bekanntlich eine Sprache, die ihre Speicherverwaltung an einen Garbage Collector übergibt und dem Programmierer hier ein weitgehend unbeschwertes Leben verschafft. Meistens.
Wer bereits in anderen GC-basierten Laufzeitumgebungen wie Java programmiert hat, der weiß sicher dass auch ein Garbage Collector Memory Leaks in manchen Fällen nicht verhindern kann wenn der Programmierer unachtsam ist. Sobald ein Objekt nämlich noch in einem aktiven Bereich referenziert wird, bleibt er bis zum entfernen dieser Referenz im Heap (immerhin besteht die Möglichkeit, dass man das Objekt noch verwendet). Das Gute: Die meisten Memory Leaks sind so unbedeutend, dass man sie getrost ignorieren kann. Kritisch wird es erst, wenn man viele größere Objekte (z.B. DOM Knoten) über einen langen Zeitraum erstellt und entfernt, auf die aber noch in irgendwelchen Codeecken verwiesen wird (z.B. in einem Array). Dann gibt es auch in JavaScript irgendwann eine OutOfMemoryException – und ohne die richtigen Tools ist deren Ursache schwer aufzufinden (immerhin kann der Bereich z.b. in einem Closure liegen).

Ich habe als Beispiel mal eine kleines Speicherfresser Snippet in JSFiddle erstellt (zu finden hier) .Ich fülle einfach ein Array mit DOM-Knoten und dieses wächst dadurch natürlich ins Unendliche.
So einfach ist es aber selten zu erkennen: Was wenn dieses Snippet in 40000 Zeilen JavaScript Code und über 5 Funktionen verteilt auftritt? Wenn die OutOfMemoryException auftritt ist es schon zu spät – und vorher bekommt man das Memory Leak nur selten mit.
Tun wir nun so als wüssten wir nicht wo der ganze Speicher gefressen wird. Daher untersuchen wir den Code mit dem Profiler, den wir in den Chrome in seinen Entwickertools mitliefert:

Legt man jetzt einen Snapshot an und untersucht ihn, sieht man im ersten Moment die recht unübersichtliche Auflistung aller Objekte und ihrer Häufigkeit. Das bringt einen nur selten zum Ziel.

Die weitaus nützlichere Sortierung ist hier ‘Dominators’, so dass die speicherintensivsten Objekte zuerst angezeigt werden. Die Auswahl der Ansichten findet man etwas versteckt im unteren Teil des Fensters. Das Vergleichen zweier  Snapshots mit ‘Comparison’ klingt zwar verlockend, bringt aber Erfahrungsgemäß keinen wirklichen Mehrwert: Auf dem Heap ist in der Regel sehr viel Dynamik vorhanden sobald die Anwendung etwas größer ist, der Memory Leak geht hier schnell zwischen den vielen Änderungen unter.
Nach dem Einstellen der Dominators-Sortierung Jetzt sieht man auch gleich unser Array an der Spitze der Objektliste (oder zumindest recht weit oben). Wichtig ist hierbei, sich an der ‘Retained Size’ zu orientieren: Diese gibt an, wie viel Speicher an dieses Objekt ‘gebunden’ ist (also freigegeben würde, wenn der Garbage Collector das Objekt aufräumt und keine weiteren Verweise auf die Objekte existieren). Shallow Size sagt nur aus, wie viel Speicher das Objekt direkt (d.h. ohne seine Referenzen) belegt.

Die Interne Adresse (nach dem @) bringt uns jetzt zwar herzlich wenig, wählt man das Objekt allerdings aus, zeigt einem der Profiler in welchen Funktionen noch auf das Objekt zugegriffen wird:
Und das sind genau die beiden Funktionen, die unseren Leak verursachen.
Gefixt ist der Code damit zwar noch nicht – aber oft ist das lokalisieren von Memory Leaks die größere Herausforderung (gerade in größeren Projekten).
Memory Leaks haben oft die nette Eigenschaft, browserübergreifend zu funktionieren: Daher lohnt es sich meistens auch bei Speicherproblemen, die in anderen Browsern auftreten mal einen Blick auf den den Chrome Profiler zu werfen.