Speicherleck bedeutet, dass kein neues Stück Speicher freigegeben oder Garbage Collection durchgeführt werden kann. In diesem Artikel werden hauptsächlich Speicherlecks bei der Verwendung von Vue vorgestellt. Freunde in Not können sich darauf beziehen
Was ist ein Speicherleck? Ein Speicherverlust bezieht sich auf einen neuen Teil des Speichers, der nicht freigegeben oder durch Müll gesammelt werden kann. Nach der Neuerstellung eines Objekts wird ein Teil des Heap-Speichers zugewiesen. Wenn der Objektzeiger auf Null gesetzt wird oder den Gültigkeitsbereich verlässt und zerstört wird, wird dieser Speicher automatisch in JS gespeichert, wenn niemand darauf verweist. Wenn der Objektzeiger jedoch nicht auf Null gesetzt ist und es keine Möglichkeit gibt, den Objektzeiger im Code abzurufen, wird der Speicher, auf den er zeigt, nicht freigegeben, was bedeutet, dass ein Speicherverlust auftritt. Warum kann dieser Objektzeiger nicht im Code abgerufen werden? Hier ist ein Beispiel:
// module date.js let date = null; export default { init () { date = new Date(); } } // main.js import date from 'date.js'; date.init();
Nachdem main.js das Datum initialisiert hat, wird die Datumsvariable zu „Es wird“. bestehen, bis Sie die Seite schließen. Da sich die Datumsreferenz in einem anderen Modul befindet, kann davon ausgegangen werden, dass das Modul ein Abschluss ist und für die Außenwelt unsichtbar ist. Wenn Sie also möchten, dass dieses Datumsobjekt immer vorhanden ist und ständig verwendet werden muss, gibt es kein Problem. Wenn Sie es jedoch einmal verwenden und dann nicht verwenden möchten, liegt ein Problem vor Der Speicher wurde nicht freigegeben, und es ist ein Speicherleck aufgetreten.
Ein weiterer subtilerer und häufiger auftretender Speicherverlust ist die Ereignisbindung, die einen Abschluss bildet und dazu führt, dass einige Variablen immer vorhanden sind. Wie im folgenden Beispiel gezeigt:
// 一个图片懒惰加载引擎示例 class ImageLazyLoader { constructor ($photoList) { $(window).on('scroll', () => { this.showImage($photoList); }); } showImage ($photoList) { $photoList.each(img => { // 通过位置判断图片滑出来了就加载 img.src = $(img).attr('src'); }); } } // 点击分页的时候就初始化一个图片懒惰加载的 $('.page').on('click', function () { new ImageLazyLoader($('img.photo')); });
Dies ist ein Modell für das verzögerte Laden von Bildern. Jedes Mal, wenn Sie auf die Seite klicken, werden die Daten auf der vorherigen Seite angezeigt wird gelöscht und auf das DOM der aktuellen Seite aktualisiert und initialisiert eine Lazy-Loading-Engine neu. Es lauscht auf das Scroll-Ereignis und verarbeitet das DOM der eingehenden Bildliste. Jedes Mal, wenn Sie auf eine neue Seite klicken, wird hier ein Speicherverlust erstellt, der hauptsächlich durch die folgenden drei Codezeilen verursacht wird:
$(window).on('scroll', () => { this.showImage($photoList); });
Da die Ereignisbindung hier in einem Abschluss definiert ist, wurden die beiden Variablen this/$photoList nicht freigegeben. Dies zeigt auf die Instanz von ImageLazyLoader und $photoList zeigt auf den DOM-Knoten relevanter DOM-Knoten Die Knoten wurden vom DOM-Baum getrennt, aber es gibt immer noch eine $photoList, die auf sie verweist. Daher können diese DOM-Knoten nicht durch Müll gesammelt werden und verbleiben im Speicher, und es kommt zu einem Speicherverlust. Da diese Variable ebenfalls durch den Abschluss abgefangen wird und nicht freigegeben wurde, liegt auch ein Speicherverlust in einer Instanz von ImageLazyLoader vor.
Die Lösung hierfür ist relativ einfach, nämlich das Deaktivieren des gebundenen Ereignisses beim Zerstören der Instanz, wie im folgenden Code gezeigt:
class ImageLazyLoader { constructor ($photoList) { this.scrollShow = () => { this.showImage($photoList); }; $(window).on('scroll', this.scrollShow); } // 新增一个事件解绑 clear () { $(window).off('scroll', this.scrollShow); } showImage ($photoList) { $photoList.each(img => { // 通过位置判断图片滑出来了就加载 img.src = $(img).attr('src'); }); // 判断如果图片已全部显示,就把事件解绑了 if (this.allShown) { this.clear(); } } } // 点击分页的时候就初始化一个图片懒惰加载的 let lazyLoader = null; $('.page').on('click', function () { lazyLoader && (lazyLoader.clear()); lazyLoader = new ImageLazyLoader($('img.photo')); });
Löschen Sie vor jeder Instanziierung eines ImageLazyLoaders die vorherige Instanz und lösen Sie die Bindung im Clear. Da JS einen Konstruktor, aber keinen Destruktor hat, müssen Sie selbst einen Clear schreiben und den Clear außerhalb manuell anpassen. Gleichzeitig wird die Bindung des Ereignisses zum entsprechenden Zeitpunkt während der Ausführung des Ereignisses automatisch aufgehoben. Das Obige dient dazu, zu beurteilen, ob das Bildlaufereignis nicht überwacht und die Bindung direkt aufgehoben werden muss. Dadurch wird das Problem von Speicherlecks gelöst und eine automatische Speicherbereinigung ausgelöst.
Warum gibt es nach der Entbindung der Veranstaltung keinen Abschlusshinweis? Da die JS-Engine erkennt, dass der Abschluss nicht mehr nützlich ist, zerstört sie den Abschluss und die externen Variablen, auf die der Abschluss verweist, bleiben natürlich leer.
Okay, die Grundkenntnisse wurden hier erklärt. Verwenden Sie nun das Speichererkennungstool von Chrome Devtools, um es erneut zu betreiben und die Erkennung einiger Speicherlecks auf der Seite zu erleichtern. Um die Auswirkungen einiger im Browser installierter Plug-Ins zu vermeiden, verwenden Sie die Inkognito-Modus-Seite von Chome, die alle Plug-Ins deaktiviert.
Öffnen Sie dann Devtools, wechseln Sie zur Registerkarte „Speicher“ und wählen Sie Heap-Snapshot aus, wie unten gezeigt:
Was ist ein Heap-Snapshot? Übersetzt handelt es sich um einen Heap-Snapshot, der ein Bild des aktuellen Speicher-Heaps erstellt. Da sich der dynamisch angewendete Speicher im Heap befindet und die lokalen Variablen im Speicherstapel liegen und vom Betriebssystem zugewiesen und verwaltet werden, kommt es zu keinen Speicherverlusten. Seien Sie also einfach besorgt über die Heap-Situation.
Führen Sie dann einige Vorgänge zum Hinzufügen, Löschen und Ändern des DOM aus, wie zum Beispiel:
(1) Öffnen Sie ein Feld und schließen Sie dann das Popup
(2 ) Klicken Sie auf eine einzelne Seite. Springen Sie zu einer anderen Route und klicken Sie dann auf „Zurück“, um zurückzukehren
(3) Klicken Sie auf „Paging“, um eine dynamische Änderung des DOM auszulösen
Das heißt, zuerst DOM hinzuzufügen und dann zu löschen Schauen Sie sich diese DOM an. Sehen Sie sich an, ob das gelöschte DOM noch Objekte enthält, die auf sie verweisen.
Hier verwende ich die zweite Methode, um zu erkennen, ob in einer Routing-Seite einer Single-Page-Anwendung ein Speicherverlust vorliegt. Öffnen Sie zuerst die Startseite, klicken Sie auf eine andere Seite, klicken Sie dann auf „Zurück“ und dann auf die Schaltfläche „Garbage Collection“:
, um die Garbage Collection auszulösen und unnötige Störungen zu vermeiden.
Dann klicken Sie auf den Foto-Button:
它就会把当前页面的内存堆扫描一遍显示出来,如下图所示:
然后在上面中间的Class Filter的搜索框里搜一下detached:
它就会显示所有已经分离了DOM树的DOM结点,重点关注distance值不为空的,这个distance表示距离DOM根结点的距离。上图展示的这些p具体是啥呢?我们把鼠标放上去不动等个2s,它就会显示这个p的DOM信息:
通过className等信息可以知道它就是那个要检查的页面的DOM节点,在下面的Object的窗口里面依次展开它的父结点,可以看到它最外面的父结点是一个VueComponent实例:
下面黄色字体native_bind表示有个事件指向了它,黄色表示引用仍然生效,把鼠标放到native_bind上面停留2秒:
它会提示你是在homework-web.vue这个文件有一个getScale函数绑定在了window上面,查看一下这个文件确实是有一个绑定:
mounted () { window.addEventListener('resize', this.getScale); }
所以虽然Vue组件把DOM删除了,但是还有个引用存在,导致组件实例没有被释放,组件里面又有一个$el指向DOM,所以DOM也没有被释放。
要在beforeDestroyed里面解绑的
beforeDestroyed () { window.removeEventListener('resize', this.getScale); }
所以综合上面的分析,造成内存泄露的可能会有以下几种情况:
(1)监听在window/body等事件没有解绑
(2)绑在EventBus的事件没有解绑
(3)Vuex的$store watch了之后没有unwatch
(4)模块形成的闭包内部变量使用完后没有置成null
(5)使用第三方库创建,没有调用正确的销毁函数
并且可以借助Chrome的内存分析工具进行快速排查,本文主要是用到了内存堆快照的基本功能,读者可以尝试分析自己的页面是否存在内存泄漏,方法是做一些操作如弹个框然后关了,拍一张堆快照,搜索detached,按distance排序,把非空的节点展开父级,找到标黄的字样说明,那些就是存在没有释放的引用。也就是说这个方法主要是分析仍然存在引用的游离DOM节点。因为页面的内存泄露通常是和DOM相关的,普通的JS变量由于有垃圾回收所以一般不会有问题,除非使用闭包把变量困住了用完了又没有置空。
DOM相关的内存泄露通常也是因为闭包和事件绑定引起的。绑了(全局)事件之后,在不需要的时候需要把它解绑。当然直接绑在p上面的可以直接把p删了,绑在它上面的事件就自然解绑了。
Das obige ist der detaillierte Inhalt vonSpeicherlecks bei der Vue-Nutzung [Empfohlen]_vue.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!