Vorwort: Da wir die Garbage Collection nicht erzwingen können, woher wissen wir, dass sie funktioniert? Und was wissen wir darüber? Die Skriptausführung wird während dieses Vorgangs angehalten. Es gibt Speicher für nicht zugängliche Ressourcen frei. Es ist nicht deterministisch. Es überprüft nicht den gesamten Speicher auf einmal, sondern wird in mehreren Zyklen ausgeführt. Es ist unvorhersehbar, wird aber bei Bedarf ausgeführt. Bedeutet dies, dass Sie sich keine Gedanken über Probleme bei der Ressourcen- und Speicherzuweisung machen müssen? Natürlich nicht. Wenn wir nicht aufpassen, kann es zu Speicherlecks kommen. Was ist ein Speicherleck? Ein Speicherleck ist ein Block zugewiesenen Speichers, den die Software nicht zurückfordern kann. Speicherverlust kann dazu führen, dass der Garbage Collector häufiger ausgeführt wird. Da dieser Vorgang die Ausführung des Skripts verhindert, kann es dazu führen, dass unser Programm hängen bleibt. Wenn es hängen bleibt, werden wählerische Benutzer es definitiv bemerken, und wenn sie damit unzufrieden sind, wird das Produkt bald offline sein. In schwerwiegenderen Fällen kann die gesamte Anwendung abstürzen, und dann wird es gg. Wie können Speicherlecks verhindert werden? Die Hauptsache ist, dass wir vermeiden sollten, unnötige Ressourcen beizubehalten. Sehen wir uns einige gängige Szenarien an. 1. Zeitüberwachung Wir erstellen eine Komponente, die eine Rückruffunktion aufruft, um zu signalisieren, dass sie nach x Schleifen fertig ist. In diesem Beispiel verwende ich importiere React, { useRef } von „react“; const Timer = ({ Zyklen, beim Ende }) => { const currentCicles = useRef(0); setzeIntervall(() => { wenn (aktuelleZyklen.aktuelle >= Zyklen) { beimFertigstellen(); zurückkehren; } aktuelleZyklen.current++; }, 500); zurückkehren ( <div>Wird geladen ...</div> ); } Standard-Timer exportieren; Auf den ersten Blick scheint es kein Problem zu geben. Keine Sorge, erstellen wir eine weitere Komponente, die diesen Timer auslöst, und analysieren ihre Speicherleistung. importiere React, { useState } von 'react'; Stile aus „../styles/Home.module.css“ importieren Timer aus „../Komponenten/Timer“ importieren; Exportieren der Standardfunktion Home() { const [showTimer, setShowTimer] = useState(); const onFinish = () => setShowTimer(false); zurückkehren ( <div Klassenname={styles.container}> {showTimer ? ( <Timer cicles={10} onFinish={onFinish} /> ): ( <button onClick={() => setzeShowTimer(true)}> Wiederholen </button> )} </div> ) } Nach einigen Klicks auf die Schaltfläche „Wiederholen“ wird dies das Ergebnis der Abfrage der Speichernutzung mit Wenn wir auf die Schaltfläche „Wiederholen“ klicken, können wir sehen, dass immer mehr Speicher zugewiesen wird. Dies bedeutet, dass der zuvor zugewiesene Speicher nicht freigegeben wurde. Der Timer läuft weiterhin, anstatt ersetzt zu werden. Wie lässt sich dieses Problem lösen? Der Rückgabewert von setInterval ist eine Intervall-ID, die wir zum Abbrechen des Intervalls verwenden können. In diesem speziellen Fall können wir useEffect(() => { const IntervallId = setInterval(() => { wenn (aktuelleZyklen.aktuelle >= Zyklen) { beimFertigstellen(); zurückkehren; } aktuelleZyklen.current++; }, 500); return () => Intervall löschen(Intervall-ID); }, []) Manchmal ist es schwierig, dieses Problem beim Schreiben von Code zu finden. Der beste Weg besteht darin, die Komponente zu abstrahieren. Wenn wir hier importiere { useEffect } von 'react'; export const useTimeout = (refreshCycle = 100, Rückruf) => { useEffect(() => { wenn (Aktualisierungszyklus <= 0) { setTimeout(Rückruf, 0); zurückkehren; } const IntervallId = setInterval(() => { Rückruf(); }, Aktualisierungszyklus); return () => Intervall löschen(Intervall-ID); }, [Aktualisierungszyklus, Intervall festlegen, Intervall löschen]); }; Standard-UseTimeout exportieren; Wenn Sie nun „setInterval“ verwenden müssen, können Sie Folgendes tun: const handleTimeout = () => ...; Zeitüberschreitung verwenden (100, Zeitüberschreitung handhaben); Jetzt können Sie diesen 2. Ereignisüberwachung In diesem Beispiel erstellen wir eine Tastenkombinationsfunktion. Da wir auf verschiedenen Seiten unterschiedliche Funktionen haben, erstellen wir unterschiedliche Tastenkombinationsfunktionen Funktion homeShortcuts({ Schlüssel}) { wenn (Schlüssel === 'E') { console.log('Widget bearbeiten') } } // Der Benutzer meldet sich auf der Homepage an, wir führen document.addEventListener('keyup', homeShortcuts); aus. // Der Benutzer führt eine Aktion aus und navigiert dann zur Einstellungsfunktion settingsShortcuts({ key}) { wenn (Schlüssel === 'E') { console.log('Einstellung bearbeiten') } } // Der Benutzer meldet sich auf der Startseite an, wir führen document.addEventListener('keyup', settingsShortcuts); aus. Es sieht immer noch gut aus, außer dass die vorherige Um den vorherigen Rückruf zu löschen, müssen wir removeEventListener verwenden: document.removeEventListener('keyup', homeShortcuts); Refaktorisieren Sie den obigen Code: Funktion homeShortcuts({ Schlüssel}) { wenn (Schlüssel === 'E') { console.log('Widget bearbeiten') } } // Benutzer landet auf Home und wir führen aus document.addEventListener('keyup', homeShortcuts); // Der Benutzer erledigt einige Aufgaben und navigiert zu den Einstellungen FunktionseinstellungenShortcuts({ key}) { wenn (Schlüssel === 'E') { console.log('Einstellung bearbeiten') } } // Benutzer landet auf Home und wir führen aus document.removeEventListener('keyup', homeShortcuts); document.addEventListener('keyup', EinstellungenShortcuts); Als Faustregel gilt: Seien Sie sehr vorsichtig, wenn Sie Tools aus dem globalen Objekt verwenden. 3.Beobachter Die Schnittstelle Obwohl es leistungsstark ist, müssen wir es mit Vorsicht einsetzen. Wenn Sie mit der Beobachtung eines Objekts fertig sind, denken Sie daran, es abzubrechen, wenn es nicht verwendet wird. Schauen Sie sich den Code an: Konstante Ref = ... const sichtbar = (sichtbar) => { console.log(`Es ist ${visible}`); } useEffect(() => { wenn (!ref) { zurückkehren; } Beobachter.aktuell = neuer IntersectionObserver( (Einträge) => { wenn (!entries[0].isIntersecting) { sichtbar (wahr); } anders { sichtbar (falsch); } }, { rootMargin: `-${header.height}px` }, ); Beobachter.aktuell.beobachten(ref); }, [ref]); Der obige Code sieht gut aus. Was passiert jedoch mit dem Beobachter, wenn die Komponente ausgehängt wird? Er wird nicht gelöscht und es kommt zu einem Speicherverlust. Wie lösen wir dieses Problem? Verwenden Sie einfach die Disconnect-Methode: Konstante Ref = ... const sichtbar = (sichtbar) => { console.log(`Es ist ${visible}`); } useEffect(() => { wenn (!ref) { zurückkehren; } Beobachter.aktuell = neuer IntersectionObserver( (Einträge) => { wenn (!entries[0].isIntersecting) { sichtbar (wahr); } anders { sichtbar (falsch); } }, { rootMargin: `-${header.height}px` }, ); Beobachter.aktuell.beobachten(ref); return () => Beobachter.aktuell?.trennen(); }, [ref]); 4. Fensterobjekt Das Hinzufügen von Objekten zu Schauen Sie sich das folgende Beispiel an: Funktion addElement(Element) { wenn (!dieser.stapel) { dieser.Stapel = { Elemente: [] } } dies.stack.elements.push(element); } Es sieht harmlos aus, aber es hängt davon ab, aus welchem Kontext Sie Ein weiteres Problem könnte die falsche Definition einer globalen Variable sein: var a = 'Beispiel 1'; // Bereich, in dem var erstellt wird b = 'Beispiel 2'; // zum Fensterobjekt hinzugefügt Um dieses Problem zu vermeiden, können Sie den strikten Modus verwenden: "streng verwenden" Durch die Verwendung des strikten Modus signalisieren Sie dem Wie sich der strikte Modus auf unser vorheriges Beispiel auswirkt:
Nicht erfasster Referenzfehler: b ist nicht definiert 5. Halten Sie die DOM-ReferenzAuch DOM-Knoten sind nicht immun gegen Speicherlecks. Wir müssen darauf achten, keine Verweise auf sie zu speichern. Andernfalls kann der Garbage Collector sie nicht bereinigen, da sie weiterhin erreichbar sind. Lassen Sie es uns mit einem kleinen Codeausschnitt demonstrieren: const-Elemente = []; const list = document.getElementById('Liste'); Funktion addElement() { // Knoten bereinigen Liste.innerHTML = ''; const divElement = document.createElement('div'); const element = document.createTextNode(`Element ${elements.length} hinzufügen`); divElement.appendChild(element); list.appendChild(divElement); Elemente.push(divElement); } document.getElementById('addElement').onclick = addElement;
Bei der nächsten Ausführung Wir überwachen die Funktion nach mehrmaliger Ausführung: Sehen Sie im Screenshot oben, wie der Knoten durchgesickert ist. Wie lässt sich das Problem beheben? Durch das Löschen Zusammenfassen: In diesem Artikel haben wir uns die häufigsten Arten von Speicherlecks angesehen. Offensichtlich verliert Sie müssen verstehen, wie Speicher und Garbage Collection in Dies ist das Ende dieses Artikels über häufige Das könnte Sie auch interessieren:
|
<<: Beispielcode zum Implementieren schöner Uhranimationseffekte mit CSS
>>: Detaillierte Erläuterung der Vorgänge und Implementierungen im Zusammenhang mit dem HTML-Druck
Zuvor habe ich zusammengefasst, wie man mit CSS di...
Inhaltsverzeichnis 1 Einleitung 2 Grundlegende Be...
Das Popup hat nichts damit zu tun, ob Ihr aktuelle...
Inhaltsverzeichnis Vorwort 1. Vorbereitung 2. Tat...
Nehmen Sie als Beispiel die WEB-Schnittstelle von...
Obwohl Microsoft später viel Forschung und Entwick...
Inhaltsverzeichnis Docker-System df Docker-System...
Inhaltsverzeichnis Typische Fälle Anhang: Häufige...
Die Wirkung ist wie folgt: Beispiel 1 Beispiel 2:...
In Front-End-Projekten ist das Hochladen von Anhä...
Nginx-Lastausgleichsserver: IP: 192.168.0.4 (Ngin...
Vorwort Kürzlich bin ich beim Upgrade von MySQL 5...
Registerkarten: Kategorie + Beschreibung Tag-Leis...
1. Einleitung MySQL verfügt über eine Replikation...
In diesem Artikel wird der spezifische Code von R...