JavaScript 記憶體洩漏檢測

Mehvish Ashiq 2024年2月15日
  1. JavaScript 中的 意外全域性變數 記憶體洩漏
  2. 使用使用嚴格;避免 JavaScript 中的記憶體洩漏
  3. 使用"use strict"; 來避免 JavaScript 中的全域性變數引起的記憶體洩漏
  4. 使用 {once: true}removeEventListener() 避免 JavaScript 中的 DOM 引用不足導致的記憶體洩漏
  5. Forgotten Callbacks or Timer 引起的記憶體洩漏及其預防技術
  6. 在 JavaScript 中如何避免被遺忘的回撥或計時器
JavaScript 記憶體洩漏檢測

不再需要記憶體洩漏的記憶體沒有返回到空閒記憶體池或作業系統。

在三種最常見的情況下可能會發生記憶體洩漏:意外全域性變數忘記的回撥或計時器DOM 引用不足

JavaScript 中的 意外全域性變數 記憶體洩漏

意外全域性變數未宣告為全域性變數。垃圾收集器無法收集它們,從而導致記憶體洩漏。

name 變數被建立為全域性 window.name,它分配的空間永遠不會被釋放。

function fn1() {
  // `name` is not declared
  name = new Array(99999999)
}
fn1();

使用使用嚴格;避免 JavaScript 中的記憶體洩漏

function fn1() {
  'use strict';
  name = new Array(99999999)
}
fn1();

輸出:

javascript 記憶體洩漏檢測-記憶體週期

未使用變數未使用,需要刪除或正確處理以釋放記憶體空間。

最關鍵的一點是找到不再需要的記憶體。

JavaScript 使用垃圾收集器來確定程式碼的某個部分是否需要記憶體。

許多垃圾收集器使用 mark-and-sweep 演算法。

使用"use strict"; 來避免 JavaScript 中的全域性變數引起的記憶體洩漏

在 Chrome DevTools 的 Memory 標籤頁上使用 heap allocations

你可以通過按 F12 或轉到 Right Click -> Inspect -> Memory 在 Chrome 中開啟 DevTools。

<!DOCTYPE html>
<html lang="en">
	<head>
 		<title>Document</title>
	</head>
	<body>
		<button id="leak-button">Start</button>
		<button id="stop-leak">Stop</button>
	<script>
    	var x=[];
    	var running = false;

    	function grow(){
 			x.push(new Array(1000000).join('x'));
 			if(running)
 				setTimeout(grow,1000);
 		}
    	$('#leak-button').click(function(){
 			running = true;
 			grow();
 		});
    	$('#stop-button').click(function(){
 			running = false;
 		});
 	</script>
 	</body>
</html>

輸出:

使用堆分配來避免記憶體洩漏

在這裡,每當使用者單擊開始按鈕並將包含百萬個 x 字元的字串推送到陣列中時,指令碼都會將 10.000 節點附加到 DOM

即使在按下停止按鈕後,垂直的部分藍線也顯示記憶體洩漏。

變數 x 是記憶體洩漏的原因,因為它是一個全域性變數,即使不再需要它也會佔用空間。

使用 {once: true}removeEventListener() 避免 JavaScript 中的 DOM 引用不足導致的記憶體洩漏

<!DOCTYPE html>
<html lang="en">
	<head>
 		<title>Document</title>
	</head>
	<body>
 		<button id="trigger">Trigger</button>
	<script>
		var clickElement = document.getElementById("click");
		const hugeString = new Array(100000).join('x');
		clickElement.addEventListener("click", function(){
            // hugeString is kept in the scope of callback forever
 			document.write(hugeString); 
		});
 	</script>
 </body>
</html>

輸出:

使用 {once: true} 和 removeEventListener() 避免記憶體洩漏

活動的事件偵聽器可以防止變數被垃圾收集。

使用 removeEventListener() 或將第三個引數作為 {once: true} 傳遞。

這樣,執行一次。listener 方法將被自動刪除。

Forgotten Callbacks or Timer 引起的記憶體洩漏及其預防技術

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Document</title>
    </head>
	<body>
	<script>
 		for (var i = 0; i < 100000; i++) {
 			var obj = {
 				call_again: function() {
 				var message = this;
 				var value = setTimeout(function() {
 					message.callAgain();
     			}, 100000);
   			}
   		}
 		obj.call_again();
 		obj = null;
   	}
	</script>
	</body>>
</html>

輸出:

由忘記回撥或定時器引起的記憶體洩漏

timer callback 和它的繫結物件 obj 直到超時結束才被釋放。

timer 可以自行重置並永遠執行。因此,記憶體空間將始終保留,永遠不會空閒。

在 JavaScript 中如何避免被遺忘的回撥或計時器

通過在 setTimeout()setInterval() 中提供參考,並在不再需要它們時直接呼叫刪除該函式。

作者: Mehvish Ashiq
Mehvish Ashiq avatar Mehvish Ashiq avatar

Mehvish Ashiq is a former Java Programmer and a Data Science enthusiast who leverages her expertise to help others to learn and grow by creating interesting, useful, and reader-friendly content in Computer Programming, Data Science, and Technology.

LinkedIn GitHub Facebook