Micro-Frontends übertragen das Konzept der Microservices in die Welt der Frontends. Anstelle eines einzigen Monolithen wird das Frontend in einzelne Komponenten zerlegt, die von eigenständigen Teams verantwortet werden. Damit wird versucht, ein großes Problem in viele kleine Teilprobleme zu unterteilen. Die Micro-Frontend-Architektur eignet sich daher besonders für große Anwendungen. Die Komplexität eines einzelnen Micro-Frontends ist deutlich geringer als bei einem Monolithen und die Komponenten sind lose gekoppelt. Dadurch können Teams leichter neue Funktionen realisieren und diese unabhängig voneinander veröffentlichen.
Die einzelnen Komponenten eines Micro-Frontends müssen in der sogenannten Komposition zusammengeführt werden. Hierfür gibt es verschiedene Strategien und Technologien. Eine Methode, die schon sehr lange existiert, ist die clientseitige Komposition mithilfe von iFrames. Dabei wird jedes Micro-Frontend innerhalb eines iFrames gerendert. iFrames bieten von Haus aus eine sehr gute Kapselung und es gibt native Methoden für die Kommunikation zwischen iFrames. Außerdem sind die Teams bei der Verwendung von iFrames frei in der Wahl der Technologie. Auf der anderen Seite sind iFrames aber auch für Performanceprobleme bekannt, und die Sicherstellung einer konsistenten User Experience stellt eine große Herausforderung dar. Eine weitere Herausforderung bei der Verwendung von iFrames sind potenzielle Speicherlecks, die durch gewisse Bad Practices bei der Entwicklung entstehen können.
Was sind Memory Leaks?
Memory Leaks oder auch Speicherlecks bezeichnen Fehler bei der Verwaltung von Speicher. Bei einem Memory Leak wird Speicher belegt, aber nicht wieder freigegeben. Dies führt dazu, dass der Speicherbedarf immer weiter steigt, was zu Programmfehlern oder sogar zu einem Absturz führen kann. Im Browser äußert sich ein Speicherleck irgendwann durch die folgende Fehlermeldung:

Glücklicherweise muss sich der Entwickler bei der Programmierung seiner Webseite nicht selbst um die Zuweisung und Freigabe von Speicher kümmern. Dies ist die Aufgabe des Garbage Collectors. Der Garbage Collector erkennt automatisch, welche Speicherbereiche nicht mehr benötigt werden und gibt diese frei.
Im Chrome-Browser (und den meisten anderen Browsern auch) werden alle Objekte so lange im Speicher gehalten, bis sie von der Garbage Collector-Wurzel (GC-Root) nicht mehr erreichbar sind. Die Objekte sind durch Referenzen miteinander verbunden. Eine solche Referenz kann zum Beispiel das Speichern eines Window-Objektes in einer Variable sein. Wenn diese Variable niemals geleert wird, wird das Window-Objekt auch im Speicher niemals freigegeben.

Memory Leaks in Micro-Frontends
In den meisten Anwendungen funktioniert der Garbage Collector des Browsers sehr gut. Objekte werden automatisch aus dem Speicher freigegeben, wenn sie nicht mehr benötigt werden, und der Entwickler muss sich nur selten explizit um die Speicherverwaltung kümmern.
Bei Micro-Frontend-Architekturen ist jedoch Vorsicht geboten. Insbesondere wenn einzelne Micro-Frontends in Form von iFrames dynamisch hinzugefügt und wieder entfernt werden, kann es schnell zu Speicherproblemen kommen.
Die Rahmenanwendung in einer Micro-Frontend-Architektur ist für die Verwaltung und Komposition der einzelnen iFrames zuständig. Oftmals werden die einzelnen Komponenten in Variablen gespeichert, um den Zugriff zu vereinfachen. In diesen Fällen gibt es eine Referenz von der Rahmenanwendung auf die untergeordneten iFrames. Wird diese Referenz nicht korrekt verworfen, verbleiben die iFrames im Speicher, was zu Speicherlecks führen kann.
Speicherlecks in Micro-Frontends: Event Listener als Risikoquelle
Aber nicht nur bei der Implementierung der Rahmenanwendung ist Vorsicht geboten. Auch bei der Implementierung der einzelnen Micro-Frontends kann es zu Speicherlecks kommen. Eine häufige Ursache sind Event Listener. Registriert die Child-Applikation einen Event Listener bei der Rahmenanwendung, erzeugt sie damit eine Referenz. Wenn diese nicht korrekt aufgehoben wird, kann dies ebenfalls zu Speicherlecks führen.
In den meisten Fällen sollten die untergeordneten Anwendungen nicht direkt auf die Rahmenanwendung zugreifen. Um Fehler zu vermeiden, sollte eine explizite Schnittstelle zwischen der Rahmenanwendung und den iFrames verwendet werden. Hierfür bietet sich die Methode postMessage() an. Dies ist ein nativer Weg der Kommunikation zwischen iFrames.
Lässt sich ein direkter Zugriff auf die Rahmenanwendung nicht vermeiden, sollte darauf geachtet werden, dass die Event Listener und andere Referenzen korrekt entfernt werden. Als Trigger für das Auflösen der Referenzen bietet sich das Event beforeunload auf dem Window-Objekt an.
Debugging von Memory Leaks
Es gibt viele Anwendungen und Tools zum Debuggen von Memory Leaks. Eines der nützlichsten Tools ist jedoch bereits direkt im Chrome-Browser enthalten: Der Heap-Profiler. Dieser befindet sich in den Chrome DevTools im Bereich Speicher. Der Heap-Profiler hilft dabei, Speicherprobleme zu untersuchen und herauszufinden, welche Objekte wie viel Speicher verbrauchen.

Im Heap-Snapshot werden für jedes Objekt zwei Größen angezeigt. Shallow Size und Retained Size. Die flache Größe (Shallow Size) zeigt die Größe des Arbeitsspeichers, die vom Objekt selbst belegt wird. Die beibehaltene Größe (Retained Size) zeigt den Arbeitsspeicher, welcher freigegeben wird, wenn das Objekt und alle seine abhängigen Objekte gelöscht werden. Dies ist der Fall, sobald das Objekt nicht mehr von der GC-Root erreichbar ist.
Es gibt viele verschiedene Arten von GC-Roots. Die meisten dieser Wurzeln sind für den Anwender nicht relevant. Für das Debuggen von Speicherlecks in Micro-Frontends sind vor allem zwei Arten von Wurzeln relevant. Das globale Fensterobjekt und der DOM-Baum des Dokuments. Beide Arten von GC-Roots sind pro iFrame vorhanden.
Speicherlecks mit dem Heap-Profiler erkennen und analysieren
Speicherprobleme in Micro-Frontends lassen sich relativ einfach mit dem Heap-Profiler identifizieren. Wenn ein Objekt nicht mehr im DOM existiert, aber noch im Speicher gehalten wird, erhält es im Heap-Profiler das Präfix “Detached”. Mit Hilfe des Filters lässt sich einfach nach diesen Objekten suchen. Wenn man auf ein Objekt klickt, erhält man unten im Abschnitt “Retainers” einen Baum mit allen Elementen, die eine Referenz auf das Objekt haben und es daher im Speicher halten. Dieser Baum kann verwendet werden, um die Ursache des nicht freigegebenen iFrames zu finden. Der Heap-Profiler bietet außerdem die Möglichkeit, direkt in den Quellcode zu schauen. Auf diese Weise ist es sehr einfach, die Stelle im Code zu finden, die den Verweis auf das iFrame enthält.
Die folgenden zwei Beispiele zeigen den Heap-Profiler für eine Anwendung, bei der im ersten Fall ein iFrame durch eine Variable und im zweiten Fall durch einen Event Listener im Speicher gehalten wird.


Wenn es keinen Verweis von einem anderen iFrame gibt, können es die DevTools selbst sein, die Objekte im Speicher halten. Um dies zu vermeiden, ist es sinnvoll, die DevTools während der Benutzung der Anwendung geschlossen zu halten und erst dann zu öffnen, wenn der Heap-Snapshot tatsächlich erstellt werden soll. Außerdem sollte die Browser-Konsole vorher geleert werden. Dies stellt sicher, dass keine Seiteneffekte auftreten.
Fazit zu Memory Leaks
Speicherlecks in Micro-Frontends können leicht auftreten, wenn sie bei der Entwicklung nicht berücksichtigt werden. Mit einem gewissen Grundverständnis des Garbage Collectors lassen sich diese Probleme jedoch leicht vermeiden. Und sollte es doch einmal zu Speicherlecks kommen, können diese mit den richtigen Werkzeugen, wie z.B. den Chrome DevTools, gut analysiert und behoben werden.
Dein Job in der Entwicklung bei d.velop
wartet auf Dich!
Entwickeln ist bei uns nicht einfach nur ein Job, sondern eine Leidenschaft. Wir stehen hinter unserem Code, den wir zusammen mit über 200 Personen in unseren agilen, crossfunktionalen Teams in der Entwicklung schaffen.