关于这方面的文章太多,综合一下写个简单的方便自己理解,针对js的内存泄漏
1、什么是内存泄漏?
内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。
2、出现情况
(1) 当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。
<div id="myDiv">
<input type="button" value="Click me" id="myBtn">
div>
<script type="text/javascript">
var btn = document.getElementById("myBtn");
btn.onclick = function(){
document.getElementById("myDiv").innerHTML = "Processing...";
}
script>
上面div里的input节点被替换了,但因为input绑定了click事件,所以ie并不会回收内存。所以要手动去除click事件。
<script type="text/javascript">
var btn = document.getElementById("myBtn");
btn.onclick = function(){
btn.onclick = null;//增加了这句
document.getElementById("myDiv").innerHTML = "Processing...";
}
script>
或者采用事件委托,把click事件绑定到父级
<div id="myDiv">
<input type="button" value="Click me" id="myBtn">
div>
<script type="text/javascript">
document.onclick = function(event){
event = event || window.event;
if(event.target.id == "myBtn"){
document.getElementById("myDiv").innerHTML = "Processing...";
}
}
script>
(2)IE7/8 DOM对象或者ActiveX对象循环引用导致内存泄漏
var a=document.getElementById("xx");
var b=document.getElementById("xxx");
a.r=b;
b.r=a;
如上述a,b,若只是他俩相互引用,并没有被其他对象引用,那么仍然会被垃圾收集系统识别并处理。但在IE中,因为循环引用中的任何对象(a、b)是 DOM 节点(或者 ActiveX 对象),垃圾收集系统则不会发现它们之间的循环关系与系统中的其他对象是隔离的并释放它们。最终它们将被保留在内存中,直到浏览器关闭。
(3)闭包有时也会产生内存泄漏。解决方法是将闭包处理函数放到外面去。如:
var leaks = (function(){
var leak = 'xxxxxx';// 被闭包所引用,不会被回收
return function(){
console.log(leak);
}
})()
或者这种闭包:
function bindEvent()
{
var obj=document.createElement("XXX");
obj.οnclick=onclickHandler; //这里将onclickHandler函数放到外面去了,本来这里是可以直接用匿名函数执行的
}
function onclickHandler(){
//do something
}
还有一种解决方法,就是在定义事件处理函数的外部函数中,删除对dom的引用
function bindEvent()
{
var obj=document.createElement("XXX");
obj.οnclick=function(){
//Even if it's a empty function
}
obj=null; //这句是增加的解决内存泄漏的方法。
}
(4) 自动类型装箱转换也会造成内存泄漏
var s=”lalala”;
alert(s.length);
s本身是一个string而非object,它没有length属性,所以当访问length时,JS引擎会自动创建一个临时String对象封装s,而这个对象一定会泄露。解决起来相当容易,记得所有值类型做.运算之前先显式转换一下:
var s="lalala";
alert(new String(s).length);
(5)全局变量引起的
function leaks(){
leak = 'xxxxxx';//leak 成为一个全局变量,不会被回收
}
(6)DOM引起的内存泄漏
当原有的DOM被移除时,子结点引用没有被移除则无法回收
var treeRef = select('#tree');
var leafRef = select('#leaf'); //在COM树中leafRef是treeFre的一个子结点
select('body').removeChild(treeRef);//#tree不能被回收入,因为treeRef还在
解决方法是先移除子节点
treeRef = null;//tree还不能被回收,因为叶子结果leafRef还在
leafRef = null;//现在#tree可以被释放了
还有一种情况,就是当动态创建的2 个不同范围的 DOM 对象附加到一起的时候,一个临时的对象会被创建。这个 DOM 对象改变范围到 document 时,那个临时对象就没用了,这个临时对象没有被回收将导致内存泄漏。如果我们一一将这两个DOM添加到原有的DOM 对象上就不会产生中间临时对象。
另外一种,则是给DOM对象添加的属性是一个对象的引用,也会引起内存泄漏。
var testObject = {};
document.getElementById('idname').property = testObject; //如果DOM不被消除,则testObject会一直存在,造成内存泄漏
需手动清除属性document.getElementById('idname').property = null; //释放内存
(7)timer定时器
setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
var val = 0;
for (var i = 0; i < 90000; i++) {
var buggyObject = {
callAgain: function() {
var ref = this;
val = setTimeout(function() {
ref.callAgain();
}, 90000);
}
}
buggyObject.callAgain();
这个时候你无法回收buggyObject buggyObject = null;
这句不会成功。
解决办法,先停止timer然后再回收
//解决方法,先停止定时器
clearTimeout(val);
buggyObject = null;
(8)Delete一个Object的属性会让此对象变慢(多耗费15倍的内存)
var o = { x: 'y' };
delete o.x; //此时o会成一个慢对象
var o = { x: 'y' };
o = null; //应该这样
3、查看内存
Chrome自带的内存调试工具可以很方便地查看内存使用情况和内存泄露:
在 Timeline -> Memory 点击record即可: