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