JS:内存泄漏

977 阅读2分钟

这篇文章主要讲讲JS中有哪些操作会导致内存泄漏。首先我理解的内存泄漏是由于代码书写不当,导致部分变量无法被JS中的垃圾回收机制清除,而永久的遗留在了内存中,从而导致内存占用变大,这就是内存泄漏。

而JS的垃圾回收机制有分有两种算法

  • 标记清除

即在一个作用域中(环境),当变量进入该环境时,就标记为进入环境,环境执行结束后,就要被标记为离开环境,因此可以被回收

  • 引用计数

即该变量目前被多少其他变量引用,当数量为0时即可被清除

导致内存泄漏的几种情况

闭包

我认为闭包是极为容易的导致内存泄漏,而且不易被发现,如果对闭包概念不了解的小伙伴可以看我另一篇文章:闭包 这里看一下代码

function f1(){
	let a = 1;
	function f2(){
		console.log(a++)
	}
	return f2
}
let fn = f1();
fn();//1
fn();//2
//....

可以看到,这里调用fn以后,输出的是不一样的结果,这说明了f1的局部变量a一直存在于内存中,没有在调用完f1以后被立刻清除,这是由于f2中存在对外层变量a的引用,因此外层作用域环境是一直都存在的,不会被回收,所以这里就造成了内存泄漏,如果是一个数据量更大的变量,那后果将不可设想。

解决方法: 将被循环引用的变量设为null即可

dom元素

当我们移除页面中的dom元素时,对dom元素的引用可能还没移除,也会导致内存泄漏

let button = document.getElementById('button');
button.click();
document.body.removeChild(button)

虽然在body中移除了这个dom,但内存中仍然保留了对这个dom的引用,如果dom中的数据量比较大,也会占用很大的内存,所以在引用完dom后,要手动将其设置为null

循环引用

在引用计数策略下,会造成内存泄漏

function fn(){
	let a = {};
	let b = {};
	a.name = b;
	b.name = a;
}
fn();

在调用完fn后,a和b的引用次数都是2,因此不会被回收,造成内存泄漏

计时器

其实计时器产生的内存泄漏和闭包有几分相似,也是占用着外层作用域不释放而造成的

var someResource = getData();//大量数据
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);

定时器内部的回调函数包含对外部数据的引用,外部变量得不到释放,会造成内存泄漏,所以定时器用完一定要记得手动清除(clearInterval)