介绍:
js中的内存垃圾回收机制:垃圾回收器会定期扫描内存,当某个内存中的值被引用为零时就会将其回收。当前变量已经使用完毕但依然被引用,导致垃圾回收器无法回收这就造成了内存泄漏。传统页面每次跳转都会释放内存,所以并不是特别明显。
如果你在用 Vue 开发应用,那么就要当心内存泄漏的问题。这个问题在单页应用 (SPA) 中尤为重要,因为在 SPA 的设计中,用户使用它时是不需要刷新浏览器的,所以 JavaScript 应用需要自行清理组件来确保垃圾回收以预期的方式生效。
内存泄漏在 Vue 应用中通常不是来自 Vue 自身的,更多地发生于把其它库集成到应用中的时候
Vue官网讲解避免内存泄露
泄漏点:
1.DOM/BOM 对象泄漏
2.script 中存在对DOM/BOM 对象的引用导致
3.Javascript 对象泄漏
4.通常由闭包导致,比如事件处理回调,导致DOM对象和脚本中对象双向引用,这个时常见的泄漏原因
代码关注点:
1.DOM中的addEventLisner 函数及派生的事件监听, 比如Jquery 中的on 函数, vue 组件实例的 $on 函数,第三方库中的初始化函数
2.其它BOM对象的事件监听, 比如websocket 实例的on 函数
3.避免不必要的函数引用
4.如果使用render 函数,避免在html标签中绑定DOM/BOM 事件
Vue如何处理:
1.如果在mounted/created 钩子中绑定了DOM/BOM 对象中的事件,需要在beforeDestroy 中做对应解绑处理
2.如果在mounted/created 钩子中使用了第三方库初始化,需要在beforeDestroy 中做对应销毁处理
3.如果组件中使用了定时器,需要在beforeDestroy 中做对应销毁处理
4.模板中不要使用表达式来绑定到特定的处理函数,这个逻辑应该放在处理函数中?
5.如果在mounted/created 钩子中使用了 o n ,需要在 b e f o r e D e s t r o y 中做对应解绑 ( on,需要在beforeDestroy 中做对应解绑( on,需要在beforeDestroy中做对应解绑(off)处理
6.某些组件在模板中使用 事件绑定可能会出现泄漏,使用$on 替换模板中的绑定
在 JavaScript 中,内存泄漏通常是由于变量、对象、闭包、事件监听器等长期存在而没有被释放引起的。这些长期存在的引用会阻止垃圾回收器回收内存,最终导致内存泄漏。
JS内存泄漏通常发生在以下情况下:
1.循环引用
当两个或多个对象之间存在相互引用,并且没有被其他对象引用,就会发生循环引用,从而导致内存泄漏。这种情况可以通过在对象之间断开引用来避免。
function createObject() {var obj1 = {};var obj2 = {};obj1.ref = obj2;obj2.ref = obj1;return obj1;
}
var myObj = createObject();
// 这里无法回收 myObj 和 myObj.ref 所占用的内存空间,导致内存泄漏
2.意外的全局变量
一个未声明变量的引用会在全局对象中创建一个新的变量。在浏览器的环境下,全局对象就是 window,也就是说:
function foo(arg) {bar = "aaaaa";
}实际上等价于
function foo(arg) {window.bar = "aaaaa";
}function foo() {this.variable = "qqqqq";
}
//this 指向全局对象(window)
foo();
为了防止这种错误的发生,可以在你的 JavaScript 文件开头添加 ‘use strict’; 语句
3.闭包
function fn1(){var n=1;
}
//我想取到里面的局部变量n
function fn1(){var n=1;function fn2(){//在加一个fn2当他的子集alert(n);}}
但是我在外面还是访问不到那就return出来
function fn1(){var n=1;function fn2(){//在加一个fn2当他的子集alert(n);}
return fn2();
//return出来后 他就给 window了所以一直存在内存中。因为一直在内存中,在IE里容易造成内存泄漏
}
fn1();
尽量书写的时候,避免这种情况。
4.定时器setTimeout setInterval
当不需要setInterval或者setTimeout时,定时器没有被clear,定时器的回调函数以及内部依赖的变量都不能被回收,造成内存泄漏。比如:vue使用了定时器,需要在beforeDestroy 中做对应销毁处理。js也是一样的。
clearTimeout(***)
clearInterval(***)
5.如果在mounted/created 钩子中使用了 o n ,需要在 b e f o r e D e s t r o y 中做对应解绑 ( on,需要在beforeDestroy 中做对应解绑( on,需要在beforeDestroy中做对应解绑(off)处理
beforeDestroy() {this.bus.$off('****');
}
6.给DOM对象添加的属性是一个对象的引用
(‘idname’).property = testObject; // 如果DOM不被消除,则testObject会一直存在,造成内存泄漏
解决方法:
在window.onunload事件中写上:
window.onunload=function(){document.getElementById('idname').property = null; //释放内存
};
7.DOM对象与JS对象相互引用
function testObject(element) { this.elementReference = element; // 为testObject(js)对象的属性绑定element(DOM)对象element.property = this; // 为element(DOM)对象的属性绑定testObject(js)对象
}
new testObject(document.getElementById('idname'));
解决方法:
在window.onunload事件中写上:
document.getElementById('idname').property = null;
8.从外到内执行appendChild。这时即使调用removeChild也无法释放
var parentDiv = document.createElement("div");
var childDiv = document.createElement("div");
document.body.appendChild(parentDiv);
parentDiv.appendChild(childDiv);
解决方法:
从内到外执行appendChild:
var parentDiv = document.createElement("div");
var childDiv = document.createElement("div");
parentDiv.appendChild(childDiv);
document.body.appendChild(parentDiv);
9.反复重写同一个属性会造成内存大量占用(但关闭IE后内存会被释放)
for(i = 0; i < 5000; i++) { hostElement.text = "asdfasdfasdf";
}
10.使用了第三方库或框架
在使用第三方库或框架时,需要确保它们没有内存泄漏问题。如果使用了存在内存泄漏问题的库或框架,就会导致整个应用程序出现内存泄漏问题。
而有时错误的使用第三方库也会导致内存泄漏,比如在定时器中循环渲染echarts:
有时我们不注意在循环中去执行下面的操作
let chart = echarts.init(document.getElementById(dom));
通过init方法创建echarts实例,如果不及时清理就会越来越多,占用大量内存,
这时我们可以这样
if (this.chart == undefined) {
this.chart = echarts.init(document.getElementById(dom));
}
或者在 init 之前销毁已经存在的 echarts 实例,可用 clear 和 dispose 方法,区别是clear()不会销毁实例,只是重新绘制图形,而dispose()会销毁实例,需要重新构建ECharts对象