Heim > Artikel > Web-Frontend > JavaScript verhindert Speicherlecks
Die Ausführung des Programms erfordert Speicher. Wann immer ein Programm danach fragt, muss das Betriebssystem oder die Laufzeit Speicher bereitstellen.
Für einen kontinuierlich laufenden Dienstprozess (Daemon) muss nicht mehr genutzter Speicher rechtzeitig freigegeben werden. Andernfalls wird die Speichernutzung immer höher, was sich im besten Fall auf die Systemleistung auswirkt und im schlimmsten Fall zum Absturz des Prozesses führt.
Speicher, der nicht mehr verwendet und nicht rechtzeitig freigegeben wird, wird als Speicherverlust bezeichnet.
Einige Sprachen (z. B. die C-Sprache) müssen Speicher manuell freigeben, und der Programmierer ist für die Speicherverwaltung verantwortlich.
char * buffer; buffer = (char*) malloc(42); // Do something with buffer free(buffer);
Das Obige ist der C-Sprachcode, der zur Beantragung von Speicher verwendet wird. Nach der Verwendung müssen Sie die kostenlose Methode verwenden, um den Speicher freizugeben.
Dies ist problematisch, daher bieten die meisten Sprachen eine automatische Speicherverwaltung, um die Belastung für Programmierer zu verringern. Dies wird als „Garbage Collector“ bezeichnet.
Woher weiß der Garbage-Collection-Mechanismus, welcher Speicher nicht mehr benötigt wird?
Die am häufigsten verwendete Methode heißt „Referenzzählung“: Die Sprach-Engine verfügt über eine „Referenztabelle“, die die Anzahl der Referenzen auf alle Ressourcen (normalerweise verschiedene Werte) im Speicher speichert. Wenn die Anzahl der Verweise auf einen Wert 0 beträgt, bedeutet dies, dass der Wert nicht mehr verwendet wird und der Speicher freigegeben werden kann.
Im Bild oben haben die beiden Werte in der unteren linken Ecke keine Referenzen und können daher freigegeben werden.
Wenn ein Wert nicht mehr benötigt wird, die Referenznummer jedoch nicht 0 ist, kann der Garbage-Collection-Mechanismus den Speicher nicht freigeben, was zu einem Speicherverlust führt.
const arr = [1, 2, 3, 4]; console.log('hello world');
Im obigen Code ist das Array [1, 2, 3, 4] ein Wert und belegt Speicher. Die Variable arr ist der einzige Verweis auf diesen Wert, daher beträgt die Anzahl der Verweise 1. Obwohl arr im folgenden Code nicht verwendet wird, belegt es weiterhin Speicher.
Wenn Sie eine Codezeile hinzufügen, um arr auf [1, 2, 3, 4] zu dereferenzieren, kann dieser Speicher durch den Garbage-Collection-Mechanismus freigegeben werden.
const arr = [1, 2, 3, 4]; console.log('hello world'); arr = null;
Wenn im obigen Code arr auf null zurückgesetzt wird, wird der Verweis auf [1, 2, 3, 4] freigegeben, die Anzahl der Verweise wird 0 und der Speicher kann freigegeben werden.
Daher bedeutet dies nicht, dass Programmierer mit dem Garbage-Collection-Mechanismus locker umgehen. Sie müssen weiterhin auf die Speichernutzung achten: Bei Werten, die viel Platz beanspruchen, müssen Sie, sobald sie nicht mehr verwendet werden, prüfen, ob noch Verweise darauf vorhanden sind. Wenn dies der Fall ist, müssen Sie es manuell dereferenzieren.
Wie kann man einen Speicherverlust beobachten?
Als Faustregel gilt: Wenn die Speichernutzung nach fünf aufeinanderfolgenden Garbage Collections jedes Mal größer wird, liegt ein Speicherverlust vor. Dies erfordert eine Echtzeitanzeige der Speichernutzung.
Um die Speichernutzung des Chrome-Browsers zu überprüfen, führen Sie die folgenden Schritte aus.
Öffnen Sie die Entwicklertools und wählen Sie das Zeitleistenfenster
aus Aktivieren Sie im Feld „Aufnahme“ oben „Speicher“
Klicken Sie auf die Aufnahmeschaltfläche in der oberen linken Ecke.
Führen Sie verschiedene Vorgänge auf der Seite aus, um die Benutzernutzung zu simulieren.
Klicken Sie nach einer gewissen Zeit auf die Schaltfläche „Stopp“ im Dialogfeld. Die Speichernutzung während dieses Zeitraums wird auf dem Bedienfeld angezeigt.
Wenn die Speichernutzung im Wesentlichen stabil ist und sich dem Niveau annähert, bedeutet dies, dass kein Speicherverlust vorliegt.
Andernfalls liegt ein Speicherverlust vor.
Die Befehlszeile kann die von Node bereitgestellte Methode „process.memoryUsage“ verwenden.
console.log(process.memoryUsage()); // { rss: 27709440, // heapTotal: 5685248, // heapUsed: 3449392, // external: 8772 }
Process.memoryUsage gibt ein Objekt zurück, das die Speichernutzungsinformationen des Node-Prozesses enthält. Dieses Objekt enthält vier Felder, die Einheit ist Byte, die Bedeutung ist wie folgt.
RSS (residente Satzgröße): Gesamte Speichernutzung, einschließlich Befehlsbereich und Stapel.
heapTotal:"堆"占用的内存,包括用到的和没用到的。
heapUsed:用到的堆的部分。
external: V8 引擎内部的 C++ 对象占用的内存。
判断内存泄漏,以heapUsed字段为准。
前面说过,及时清除引用非常重要。但是,你不可能记得那么多,有时候一疏忽就忘了,所以才有那么多内存泄漏。
最好能有一种方法,在新建引用的时候就声明,哪些引用必须手动清除,哪些引用可以忽略不计,当其他引用消失以后,垃圾回收机制就可以释放内存。这样就能大大减轻程序员的负担,你只要清除主要引用就可以了。
ES6 考虑到了这一点,推出了两种新的数据结构:WeakSet 和 WeakMap。它们对于值的引用都是不计入垃圾回收机制的,所以名字里面才会有一个"Weak",表示这是弱引用。
下面以 WeakMap 为例,看看它是怎么解决内存泄漏的。
const wm = new WeakMap(); const element = document.getElementById('example'); wm.set(element, 'some information'); wm.get(element) // "some information"
上面代码中,先新建一个 Weakmap 实例。然后,将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在 WeakMap 里面。这时,WeakMap 里面对element的引用就是弱引用,不会被计入垃圾回收机制。
也就是说,DOM 节点对象的引用计数是1,而不是2。这时,一旦消除对该节点的引用,它占用的内存就会被垃圾回收机制释放。Weakmap 保存的这个键值对,也会自动消失。
基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。
WeakMap 的例子很难演示,因为无法观察它里面的引用会自动消失。此时,其他引用都解除了,已经没有引用指向 WeakMap 的键名了,导致无法证实那个键名是不是存在。
我一直想不出办法,直到有一天贺师俊老师提示,如果引用所指向的值占用特别多的内存,就可以通过process.memoryUsage方法看出来。
根据这个思路,网友 vtxf 补充了下面的例子。
首先,打开 Node 命令行。
$ node --expose-gc
上面代码中,--expose-gc参数表示允许手动执行垃圾回收机制。
然后,执行下面的代码。
// 手动执行一次垃圾回收,保证获取的内存使用状态准确 > global.gc(); undefined // 查看内存占用的初始状态,heapUsed 为 4M 左右 > process.memoryUsage(); { rss: 21106688, heapTotal: 7376896, heapUsed: 4153936, external: 9059 } > let wm = new WeakMap(); undefined > const b = new Object(); undefined > global.gc(); undefined // 此时,heapUsed 仍然为 4M 左右 > process.memoryUsage(); { rss: 20537344, heapTotal: 9474048, heapUsed: 3967272, external: 8993 } // 在 WeakMap 中添加一个键值对, // 键名为对象 b,键值为一个 5*1024*1024 的数组 > wm.set(b, new Array(5*1024*1024)); WeakMap {} // 手动执行一次垃圾回收 > global.gc(); undefined // 此时,heapUsed 为 45M 左右 > process.memoryUsage(); { rss: 62652416, heapTotal: 51437568, heapUsed: 45911664, external: 8951 } // 解除对象 b 的引用 > b = null; null // 再次执行垃圾回收 > global.gc(); undefined // 解除 b 的引用以后,heapUsed 变回 4M 左右 // 说明 WeakMap 中的那个长度为 5*1024*1024 的数组被销毁了 > process.memoryUsage(); { rss: 20639744, heapTotal: 8425472, heapUsed: 3979792, external: 8956 }
上面代码中,只要外部的引用消失,WeakMap 内部的引用,就会自动被垃圾回收清除。由此可见,有了它的帮助,解决内存泄漏就会简单很多。
Simple Guide to Finding a JavaScript Memory Leak in Node.js
Understanding Garbage Collection and hunting Memory Leaks in Node.js
Debugging Memory Leaks in Node.js Applications
Das obige ist der detaillierte Inhalt vonJavaScript verhindert Speicherlecks. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!