什么是内存泄露
内存泄露也称作"存储泄漏",用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元,直到程序结束。简单说就是内存空间使用完毕之后未回收。
内存泄露的常见场景:
循环引用
当两个或多个对象彼此引用时,即使没有其它代码引用他们,即使没有其它代码引用他们,他们也无法被回收。这种情况常见于对象之间的相互引用或闭包的引用
// 就如上文垃圾回收机制说的引用计数法导致的循环引用问题一样,两个或多个对象彼此引用导致内存泄漏
// 内存泄漏示例
function fun(){
var objA={};
var objB={};
objA.oneProp=objB;
objB.anoterProp=objA;
}
// 正确写法
function fun(){
var objA={};
var objB={};
objA.oneProp=objB;
// 修改为null或undefined
objB.anoterProp=null;
// 或者objB.anoterProp=undefined;
}
解决:改为null或undefined
闭包
闭包引起的内存泄漏是指由于引用链的存在,变量无法被垃圾回收机制及时释放造成的内存浪费问题。当一个函数引用了外部的变量,并且该函数被其他对象所引用时,就会形成一个闭包,而这个闭包会保留对外部变量的引用,使得这些变量无法被垃圾回收机制回收。
function createClosure() {
var bigData = new Array(1000000).join('*');
return function() {
console.log(bigData);
}
}
var myClosure = createClosure();
// 正确写法
// 如果不手动解除引用 myClosure 并不能被垃圾回收 导致 bigData 内存无法释放
myClosure = null;
解决:手动解除引用,在不需要使用闭包时,可以显式解除对闭包的引用。将闭包赋值为 null 或者其他非引用值,以便让垃圾回收机制回收相关资源
意外的全局变量
JS中未声明的变量会默认定义在全局变量window上,只有关闭窗口或者刷新页面时,全局变量才会被释放。如果未声明的变量存储了大量的数据,这样就会造成内存泄露
// 内存泄漏示例
function fun(){
globalVar='全局变量';
}
// 正确写法
function fun(){
// 使用var声明变量,在方法内部是局部变量,在方法外部是全局变量
var localVar='局部变量';
}
解决:使用var、let或const声明的变量,避免创建全局变量
定时器未清理
如果在使用定时器函数,未正确清除时,定时器的函数或闭包会一直存在内存中,直到定时器被取消或到期
// 内存泄漏示例
function timer(){
setInterval(function(){
console.log(1);
},1000)
}
//正确写法
var timerId=setInterval(function(){
console.log(1);
})
// 清除定时器
clearInterval(timerId);
解决:清除定时器:clearTimeout()或clearInterval()
DOM元素的引用
在JavaScript中,对DOM元素的引用会阻止该元素被垃圾回收,如果不再需要这些引用而没有及时解绑,就可能导致内存泄露
// 内存泄漏示例
var element = document.getElementById('myElement');
var references = [];
function createReference() {
references.push(element);
}
// 正确写法
var element = document.getElementById('myElement');
var references = [];
function createReference() {
references.push(element);
}
// 移除引用事件
function removeReference() {
references.splice(references.indexOf(element), 1);
}
解决:及时移除引用事件
事件监听
同一个事件重复监听,但是忘记移除,会导致内存泄露
// 内存泄漏示例
var element = document.getElementById('myElement');
element.addEventListener('click', function handleClick() {
console.log(1)
});
// 正确写法
var element = document.getElementById('myElement');
function handleClick() {
console.log(1)
}
element.addEventListener('click', handleClick);
// 在不需要监听事件时,解除绑定
element.removeEventListener('click', handleClick);
解决:及时在不需要监听事件时,移除监听事件