Speicherlecks bei getrennten Fenstern

Finden und beseitigen knifflige Speicherlecks, die durch nicht getrennte Fenster verursacht werden.

Bartek Nowierski
Bartek Nowierski

Was ist ein Speicherleck in JavaScript?

Ein Speicherleck ist eine unbeabsichtigte Erhöhung des von einer App genutzten Arbeitsspeichers im Laufe der Zeit. In JavaScript treten Speicherlecks auf, wenn Objekte nicht mehr benötigt werden, aber weiterhin von Funktionen oder anderen Objekten referenziert werden. Diese Verweise verhindern, dass die nicht benötigten Objekte von der automatischen Speicherbereinigung zurückgefordert werden.

Die Aufgabe der automatischen Speicherbereinigung besteht darin, Objekte zu identifizieren und zurückzufordern, die nicht mehr über die Anwendung erreichbar sind. Dies funktioniert auch dann, wenn Objekte auf sich selbst oder zyklisch aufeinander verweisen. Sobald keine Verweise mehr vorhanden sind, über die eine Anwendung auf eine Gruppe von Objekten zugreifen könnte, kann eine automatische Speicherbereinigung durchgeführt werden.

let A = {};
console.log(A); // local variable reference

let B = {A}; // B.A is a second reference to A

A = null; // unset local variable reference

console.log(B.A); // A can still be referenced by B

B.A = null; // unset B's reference to A

// No references to A are left. It can be garbage collected.

Eine besonders schwierige Klasse von Speicherlecks tritt auf, wenn eine Anwendung auf Objekte mit einem eigenen Lebenszyklus verweist, z. B. DOM-Elemente oder Pop-up-Fenster. Es kann vorkommen, dass diese Objekttypen ohne das Wissen der Anwendung ungenutzt werden. Das bedeutet, dass der Anwendungscode möglicherweise die einzigen verbleibenden Verweise auf ein Objekt hat, das andernfalls durch die automatische Speicherbereinigung gelöscht werden könnte.

Was ist ein freistehendes Fenster?

Im folgenden Beispiel enthält eine Diashow-Viewer-Anwendung Schaltflächen zum Öffnen und Schließen eines Pop-up-Fensters mit Vortragsnotizen. Angenommen, ein Nutzer klickt auf Notizen anzeigen und schließt das Pop-up-Fenster direkt, anstatt auf die Schaltfläche Notizen ausblenden zu klicken. Die Variable notesWindow enthält weiterhin einen Verweis auf das Pop-up, auf das zugegriffen werden kann, obwohl es nicht mehr verwendet wird.

<button id="show">Show Notes</button>
<button id="hide">Hide Notes</button>
<script type="module">
  let notesWindow;
  document.getElementById('show').onclick = () => {
    notesWindow = window.open('/presenter-notes.html');
  };
  document.getElementById('hide').onclick = () => {
    if (notesWindow) notesWindow.close();
  };
</script>

Dies ist ein Beispiel für ein getrenntes Fenster. Das Pop-up-Fenster wurde geschlossen, aber der Code verweist darauf, dass der Browser es nicht zerstören und Arbeitsspeicher freigeben kann.

Wenn eine Seite window.open() aufruft, um ein neues Browserfenster oder einen neuen Tab zu erstellen, wird ein Window-Objekt zurückgegeben, das das Fenster oder den Tab darstellt. Auch wenn ein solches Fenster geschlossen oder der Nutzer es verlassen hat, kann das von window.open() zurückgegebene Window-Objekt weiterhin für den Zugriff auf Informationen dazu verwendet werden. Dies ist eine Art von separatem Fenster: Da JavaScript-Code weiterhin möglicherweise auf Attribute des geschlossenen Window-Objekts zugreifen kann, muss es im Arbeitsspeicher gehalten werden. Wenn das Fenster viele JavaScript-Objekte oder iFrames enthielt, kann dieser Arbeitsspeicher erst freigegeben werden, wenn keine JavaScript-Verweise mehr auf die Fenstereigenschaften mehr vorhanden sind.

Chrome-Entwicklertools verwenden, um zu zeigen, wie ein Dokument aufbewahrt wird, nachdem ein Fenster geschlossen wurde.

Das gleiche Problem kann auch bei der Verwendung von <iframe>-Elementen auftreten. iFrames verhalten sich wie verschachtelte Fenster, die Dokumente enthalten, und ihre contentWindow-Eigenschaft bietet Zugriff auf das enthaltene Window-Objekt, ähnlich wie der von window.open() zurückgegebene Wert. Der JavaScript-Code kann auch dann auf die contentWindow oder contentDocument eines iFrames verweisen, wenn der iFrame aus dem DOM entfernt oder die URL geändert wird. Dadurch wird verhindert, dass das Dokument automatisch bereinigt wird, da weiterhin auf seine Eigenschaften zugegriffen werden kann.

Demonstration, wie ein Event-Handler das Dokument eines iFrames beibehalten kann, auch nachdem aus dem iFrame zu einer anderen URL gewechselt wurde.

Falls ein Verweis auf document in einem Fenster oder iFrame von JavaScript beibehalten wird, bleibt dieses Dokument im Arbeitsspeicher, auch wenn das enthaltende Fenster oder iFrame zu einer neuen URL wechselt. Dies kann besonders lästig sein, wenn das JavaScript mit dieser Referenz nicht erkennt, dass das Fenster/Frame zu einer neuen URL navigiert ist, da nicht weiß, wann dies die letzte Referenz ist, bei der ein Dokument gespeichert wird.

So verursachen getrennte Fenster Speicherlecks

Wenn Sie mit Fenstern und iFrames arbeiten, die sich auf derselben Domain wie die primäre Seite befinden, wird häufig auf Ereignisse gewartet oder auf Attribute über Dokumentgrenzen hinweg zugegriffen. Sehen wir uns z. B. eine Variante des Präsentationsbetrachters vom Anfang dieses Leitfadens an. Daraufhin öffnet sich ein zweites Fenster für die Vortragsnotizen. Im Fenster für Vortragsnotizen wird auf click-Ereignisse gewartet, um zur nächsten Folie zu wechseln. Wenn der Nutzer dieses Notizenfenster schließt, hat der JavaScript-Code, der im ursprünglichen übergeordneten Fenster ausgeführt wird, weiterhin vollen Zugriff auf das Dokument mit den Vortragsnotizen:

<button id="notes">Show Presenter Notes</button>
<script type="module">
  let notesWindow;
  function showNotes() {
    notesWindow = window.open('/presenter-notes.html');
    notesWindow.document.addEventListener('click', nextSlide);
  }
  document.getElementById('notes').onclick = showNotes;

  let slide = 1;
  function nextSlide() {
    slide += 1;
    notesWindow.document.title = `Slide  ${slide}`;
  }
  document.body.onclick = nextSlide;
</script>

Angenommen, wir schließen das von showNotes() oben erstellte Browserfenster. Es gibt keinen Event-Handler, der darauf wartet, dass das Fenster geschlossen wurde. Daher wird unser Code nicht darüber informiert, dass Verweise auf das Dokument bereinigt werden sollten. Die Funktion nextSlide() ist immer noch „live“, da sie als Klick-Handler auf unserer Hauptseite gebunden ist. Die Tatsache, dass nextSlide einen Verweis auf notesWindow enthält, bedeutet, dass weiterhin auf das Fenster verwiesen wird und keine automatische Speicherbereinigung möglich ist.

Abbildung, wie Verweise auf ein Fenster verhindern, dass es nach dem Schließen einer automatischen Speicherbereinigung unterzogen wird.

Es gibt eine Reihe anderer Szenarien, in denen Verweise versehentlich beibehalten werden und so verhindern, dass getrennte Fenster für die automatische Speicherbereinigung infrage kommen:

  • Event-Handler können im ersten Dokument eines iFrames registriert werden, bevor der Frame zur gewünschten URL wechselt. Dadurch entstehen versehentliche Verweise auf das Dokument und der iFrame, nachdem andere Verweise bereinigt wurden.

  • Ein Dokument mit viel Arbeitsspeicher, das in einem Fenster oder iFrame geladen wird, kann versehentlich lange im Arbeitsspeicher verbleiben, nachdem zu einer neuen URL gewechselt wurde. Dies wird häufig dadurch verursacht, dass die übergeordnete Seite Verweise auf das Dokument beibehält, um das Entfernen des Listeners zu ermöglichen.

  • Wenn ein JavaScript-Objekt an ein anderes Fenster oder iFrame übergeben wird, enthält die Prototypkette des Objekts Verweise auf die Umgebung, in der es erstellt wurde, einschließlich des Fensters, in dem es erstellt wurde. Das bedeutet, dass es genauso wichtig ist, Verweise auf Objekte aus anderen Fenstern nicht an sich zu binden, als auch Verweise auf die Fenster selbst.

    index.html:

    <script>
      let currentFiles;
      function load(files) {
        // this retains the popup:
        currentFiles = files;
      }
      window.open('upload.html');
    </script>
    

    upload.html:

    <input type="file" id="file" />
    <script>
      file.onchange = () => {
        parent.load(file.files);
      };
    </script>
    

Speicherlecks aufgrund getrennter Fenster erkennen

Das Aufspüren von Speicherlecks kann schwierig sein. Oft ist es schwierig, isolierte Reproduktionen dieser Probleme zu konstruieren, insbesondere wenn mehrere Dokumente oder Fenster beteiligt sind. Um die Sache zu komplizierter zu machen, kann das Prüfen potenzieller gehackter Referenzen dazu führen, dass zusätzliche Referenzen erstellt werden, die verhindern, dass die geprüften Objekte automatisch bereinigt werden. Daher ist es sinnvoll, mit Tools zu beginnen, die diese Möglichkeit vermeiden.

Ein guter Ausgangspunkt für die Fehlerbehebung bei Speicherproblemen ist das Erstellen eines Heap-Snapshots. Dadurch erhalten Sie eine Ansicht des derzeit von einer Anwendung verwendeten Arbeitsspeichers zu einem bestimmten Zeitpunkt. Das umfasst alle Objekte, die zwar erstellt, aber noch nicht automatisch bereinigt wurden. Heap-Snapshots enthalten nützliche Informationen zu Objekten, z. B. deren Größe und eine Liste der Variablen und Schließungen, die auf sie verweisen.

Screenshot eines Heap-Snapshots in den Chrome-Entwicklertools mit den Referenzen, die ein großes Objekt enthalten.
Ein Heap-Snapshot mit den Referenzen, die ein großes Objekt enthalten.

Wenn Sie einen Heap-Snapshot aufzeichnen möchten, rufen Sie in den Chrome-Entwicklertools den Tab Arbeitsspeicher auf und wählen Sie in der Liste der verfügbaren Profilerstellungstypen Heap-Snapshot aus. Sobald die Aufzeichnung abgeschlossen ist, werden in der Zusammenfassungsansicht die aktuellen Objekte im Arbeitsspeicher angezeigt, gruppiert nach Konstruktor.

Demonstration der Erstellung eines Heap-Snapshots in den Chrome-Entwicklertools.

Das Analysieren von Heap-Dumps kann eine gewaltige Aufgabe sein und es kann im Rahmen der Fehlerbehebung ziemlich schwierig sein, die richtigen Informationen zu finden. Zu diesem Zweck haben die Chromium-Entwickler yossik@ und peledni@ ein eigenständiges Tool Heap Cleaner entwickelt, mit dem ein bestimmter Knoten wie ein abgetrenntes Fenster hervorgehoben werden kann. Wenn Sie den Heap Cleaner für einen Trace ausführen, werden andere unnötige Informationen aus dem Aufbewahrungsdiagramm entfernt, wodurch der Trace Cleaner viel einfacher und leichter zu lesen ist.

Arbeitsspeicher programmatisch messen

Heap-Snapshots bieten ein hohes Maß an Detailgenauigkeit und eignen sich hervorragend, um herauszufinden, wo Lecks auftreten. Das Erstellen eines Heap-Snapshots ist jedoch ein manueller Prozess. Eine andere Möglichkeit zur Prüfung auf Speicherlecks besteht darin, die aktuell verwendete JavaScript-Heap-Größe aus der performance.memory API abzurufen:

Screenshot eines Abschnitts der Benutzeroberfläche der Chrome-Entwicklertools.
Überprüfung der verwendeten JS-Heap-Größe in den Entwicklertools, da ein Pop-up erstellt, geschlossen und nicht referenziert wird.

Die performance.memory API liefert nur Informationen zur JavaScript-Heap-Größe, d. h. sie enthält nicht den vom Dokument und den Ressourcen des Pop-ups verwendeten Arbeitsspeicher. Dazu müssten wir die neue performance.measureUserAgentSpecificMemory() API verwenden, die derzeit in Chrome getestet wird.

Lösungen zur Vermeidung von Lecks bei abgetrennten Fenstern

Die beiden häufigsten Fälle, in denen getrennte Fenster zu Speicherlecks führen, sind, wenn das übergeordnete Dokument Verweise auf ein geschlossenes Pop-up oder einen entfernten iFrame enthält und wenn eine unerwartete Navigation in einem Fenster oder iFrame dazu führt, dass die Ereignis-Handler nie unregistriert werden.

Beispiel: Pop-up schließen

Im folgenden Beispiel werden zwei Schaltflächen zum Öffnen und Schließen eines Pop-up-Fensters verwendet. Damit die Schaltfläche Popup schließen funktioniert, wird ein Verweis auf das geöffnete Pop-up-Fenster in einer Variablen gespeichert:

<button id="open">Open Popup</button>
<button id="close">Close Popup</button>
<script>
  let popup;
  open.onclick = () => {
    popup = window.open('/login.html');
  };
  close.onclick = () => {
    popup.close();
  };
</script>

Auf den ersten Blick scheint es, als ob der obige Code häufige Fallstricke vermeidet: Es werden keine Verweise auf das Pop-up-Dokument beibehalten und es werden keine Event-Handler im Pop-up-Fenster registriert. Sobald jedoch auf die Schaltfläche Pop-up öffnen geklickt wird, verweist die Variable popup jetzt auf das geöffnete Fenster. Die Variable kann über den Klick-Handler für die Schaltfläche Pop-up schließen aufgerufen werden. Wenn popup nicht neu zugewiesen oder der Klick-Handler entfernt wird, kann der eingeschlossene Verweis auf popup des Handlers nicht automatisch bereinigt werden.

Lösung: Verweise aufheben

Variablen, die auf ein anderes Fenster oder das zugehörige Dokument verweisen, führen dazu, dass es im Arbeitsspeicher beibehalten wird. Da Objekte in JavaScript immer Verweise sind, wird der Verweis auf das ursprüngliche Objekt entfernt, wenn Sie Variablen einen neuen Wert zuweisen. Um Verweise auf ein Objekt aufzuheben, können Sie diese Variablen dem Wert null neu zuweisen.

Wenn wir dies auf das vorherige Pop-up-Beispiel anwenden, können wir den Handler für die Schließen-Schaltfläche ändern, um seinen Verweis auf das Pop-up-Fenster aufzuheben:

let popup;
open.onclick = () => {
  popup = window.open('/login.html');
};
close.onclick = () => {
  popup.close();
  popup = null;
};

Dies ist hilfreich, zeigt jedoch ein weiteres Problem speziell für Fenster auf, die mit open() erstellt wurden: Was passiert, wenn der Nutzer das Fenster schließt, anstatt auf unsere benutzerdefinierte Schließen-Schaltfläche zu klicken? Und was geschieht, wenn der Nutzer in dem geöffneten Fenster andere Websites aufruft? Ursprünglich schien es ausreichend zu sein, die popup-Referenz beim Klicken auf die Schließen-Schaltfläche zu entfernen, aber es gibt immer noch ein Speicherleck, wenn Nutzer diese bestimmte Schaltfläche nicht zum Schließen des Fensters verwenden. Um dieses Problem zu lösen, müssen diese Fälle erkannt werden, um anhaltende Referenzen beim Auftreten zu deaktivieren.

Lösung: Überwachung und Entsorgung

In vielen Fällen hat das JavaScript für das Öffnen von Fenstern oder das Erstellen von Frames keine exklusive Kontrolle über ihren Lebenszyklus. Pop-ups können vom Nutzer geschlossen werden oder die Navigation zu einem neuen Dokument kann dazu führen, dass das Dokument, das zuvor in einem Fenster oder Frame enthalten war, getrennt wird. In beiden Fällen löst der Browser ein pagehide-Ereignis aus, um zu signalisieren, dass das Dokument entladen wird.

Mit dem Ereignis pagehide können geschlossene Fenster und die Navigation weg vom aktuellen Dokument erkannt werden. Es gibt jedoch eine wichtige Einschränkung: Alle neu erstellten Fenster und iFrames enthalten ein leeres Dokument. Rufen Sie dann asynchron die angegebene URL auf, falls diese angegeben wird. Daher wird ein anfängliches pagehide-Ereignis kurz nach dem Erstellen des Fensters oder Frames ausgelöst, kurz bevor das Zieldokument geladen wurde. Da unser Referenzbereinigungscode beim Entladen des target-Dokuments ausgeführt werden muss, müssen wir dieses erste pagehide-Ereignis ignorieren. Dafür gibt es eine Reihe von Techniken. Die einfachste Methode besteht darin, Pagehide-Ereignisse zu ignorieren, die von der about:blank-URL des ursprünglichen Dokuments ausgehen. In unserem Pop-up-Beispiel würde das so aussehen:

let popup;
open.onclick = () => {
  popup = window.open('/login.html');

  // listen for the popup being closed/exited:
  popup.addEventListener('pagehide', () => {
    // ignore initial event fired on "about:blank":
    if (!popup.location.host) return;

    // remove our reference to the popup window:
    popup = null;
  });
};

Diese Methode funktioniert nur für Fenster und Frames, die denselben effektiven Ursprung wie die übergeordnete Seite haben, auf der unser Code ausgeführt wird. Beim Laden von Inhalten aus einem anderen Ursprung sind sowohl location.host als auch das Ereignis pagehide aus Sicherheitsgründen nicht verfügbar. Im Allgemeinen empfiehlt es sich, Verweise auf andere Ursprünge zu vermeiden. In seltenen Fällen, in denen dies erforderlich ist, ist es jedoch möglich, die Attribute window.closed oder frame.isConnected zu überwachen. Wenn sich diese Eigenschaften ändern, um auf ein geschlossenes Fenster oder einen entfernten iFrame hinzuweisen, sollten Sie alle Verweise darauf aufheben.

let popup = window.open('https://example.com');
let timer = setInterval(() => {
  if (popup.closed) {
    popup = null;
    clearInterval(timer);
  }
}, 1000);

Lösung: WeakRef verwenden

JavaScript unterstützt seit Kurzem eine neue Möglichkeit, Objekte zu referenzieren, die eine automatische Speicherbereinigung ermöglicht: WeakRef. Eine für ein Objekt erstellte WeakRef ist kein direkter Verweis, sondern ein separates Objekt mit einer speziellen .deref()-Methode, die einen Verweis auf das Objekt zurückgibt, sofern die Speicherbereinigung nicht durchgeführt wurde. Mit WeakRef ist es möglich, auf den aktuellen Wert eines Fensters oder Dokuments zuzugreifen, aber trotzdem die automatische Speicherbereinigung zuzulassen. Anstatt einen Verweis auf das Fenster beizubehalten, das manuell als Reaktion auf Ereignisse wie pagehide oder Attribute wie window.closed aufgehoben werden muss, wird der Zugriff auf das Fenster nach Bedarf gewährt. Wenn das Fenster geschlossen ist, kann es durch die automatische Speicherbereinigung bereinigt werden, sodass die Methode .deref() undefined zurückgibt.

<button id="open">Open Popup</button>
<button id="close">Close Popup</button>
<script>
  let popup;
  open.onclick = () => {
    popup = new WeakRef(window.open('/login.html'));
  };
  close.onclick = () => {
    const win = popup.deref();
    if (win) win.close();
  };
</script>

Ein interessantes Detail, das Sie bei der Verwendung von WeakRef für den Zugriff auf Fenster oder Dokumente berücksichtigen sollten, ist, dass der Verweis in der Regel für kurze Zeit verfügbar bleibt, nachdem das Fenster geschlossen oder der iFrame entfernt wurde. Das liegt daran, dass WeakRef so lange einen Wert zurückgibt, bis die automatische Speicherbereinigung für das zugehörige Objekt abgeschlossen ist. Dies geschieht asynchron in JavaScript und in der Regel bei Inaktivität. Glücklicherweise löst das Erstellen eines Heap-Snapshots bei der Suche nach getrennten Fenstern im Bereich Arbeitsspeicher der Chrome-Entwicklertools die automatische Speicherbereinigung aus und löscht das schwach referenzierte Fenster. Außerdem können Sie prüfen, ob ein Objekt, auf das über WeakRef verwiesen wird, aus JavaScript entfernt wurde. Dazu können Sie entweder feststellen, ob deref() undefined zurückgibt, oder die neue FinalizationRegistry API verwenden:

let popup = new WeakRef(window.open('/login.html'));

// Polling deref():
let timer = setInterval(() => {
  if (popup.deref() === undefined) {
    console.log('popup was garbage-collected');
    clearInterval(timer);
  }
}, 20);

// FinalizationRegistry API:
let finalizers = new FinalizationRegistry(() => {
  console.log('popup was garbage-collected');
});
finalizers.register(popup.deref());

Lösung: Kommunikation über postMessage

Wenn erkannt wird, wenn Fenster geschlossen sind oder die Navigation ein Dokument entlädt, können wir Handler entfernen und Verweise aufheben, damit getrennte Fenster automatisch bereinigt werden können. Diese Änderungen sind jedoch spezifische Korrekturen für ein wesentlicheres Problem: die direkte Kopplung zwischen Seiten.

Es ist ein ganzheitlicherer alternativer Ansatz verfügbar, der veraltete Verweise zwischen Fenstern und Dokumenten vermeidet. Durch die Beschränkung der dokumentenübergreifenden Kommunikation auf postMessage() wird eine Trennung hergestellt. Im Erinnern an unser ursprüngliches Beispiel für Vortragsnotizen haben Funktionen wie nextSlide() das Notizenfenster direkt aktualisiert, indem sie darauf verwiesen und den Inhalt geändert haben. Stattdessen könnte die primäre Seite die erforderlichen Informationen asynchron und indirekt über postMessage() an das Notizenfenster übergeben.

let updateNotes;
function showNotes() {
  // keep the popup reference in a closure to prevent outside references:
  let win = window.open('/presenter-view.html');
  win.addEventListener('pagehide', () => {
    if (!win || !win.location.host) return; // ignore initial "about:blank"
    win = null;
  });
  // other functions must interact with the popup through this API:
  updateNotes = (data) => {
    if (!win) return;
    win.postMessage(data, location.origin);
  };
  // listen for messages from the notes window:
  addEventListener('message', (event) => {
    if (event.source !== win) return;
    if (event.data[0] === 'nextSlide') nextSlide();
  });
}
let slide = 1;
function nextSlide() {
  slide += 1;
  // if the popup is open, tell it to update without referencing it:
  if (updateNotes) {
    updateNotes(['setSlide', slide]);
  }
}
document.body.onclick = nextSlide;

Dies erfordert zwar weiterhin, dass die Fenster aufeinander verweisen, aber keiner von beiden behält einen Verweis auf das aktuelle Dokument von einem anderen Fenster bei. Ein Ansatz zur Nachrichtenweitergabe empfiehlt auch Designs, bei denen Fensterbezüge an einer einzigen Stelle gehalten werden, was bedeutet, dass nur ein Bezug aufgehoben werden muss, wenn Fenster geschlossen werden oder weggeleitet werden. Im obigen Beispiel behält nur showNotes() einen Verweis auf das Notizenfenster bei und verwendet das Ereignis pagehide, um dafür zu sorgen, dass der Verweis bereinigt wird.

Lösung: Verweise mit noopener vermeiden

Wenn ein Pop-up-Fenster geöffnet wird, mit dem Ihre Seite nicht kommunizieren oder steuern muss, können Sie möglicherweise vermeiden, dass ein Verweis auf das Fenster abgerufen wird. Dies ist besonders nützlich, wenn Sie Fenster oder iFrames erstellen, über die Inhalte von einer anderen Website geladen werden. In diesen Fällen akzeptiert window.open() eine "noopener"-Option, die wie das rel="noopener"-Attribut für HTML-Links funktioniert:

window.open('https://example.com/share', null, 'noopener');

Die Option "noopener" sorgt dafür, dass window.open() den Wert null zurückgibt. Es ist also nicht möglich, versehentlich einen Verweis auf das Pop-up zu speichern. Außerdem wird verhindert, dass das Pop-up-Fenster einen Verweis auf das übergeordnete Fenster erhält, da das Attribut window.opener den Wert null hat.

Feedback

Wir hoffen, dass einige der Vorschläge in diesem Artikel Ihnen beim Auffinden und Beheben von Speicherlecks helfen. Wenn Sie eine andere Methode zur Fehlerbehebung bei getrennten Fenstern haben oder dieser Artikel Ihnen beim Aufdecken von Speicherlecks in Ihrer App geholfen hat, würde ich mich sehr freuen. Sie finden mich auf Twitter @_developit.