想解决内存泄露问题,必须知道什么是内存泄露,什么情况下出现内存泄露,才能在遇到问题时,逐个排除。这里只讨论那些不经意间的内存泄露。
一、什么是内存泄露
内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。在C++中,因为是手动管理内存,内存泄露是经常出现的事情。而现在流行的C#和Java等语言采用了自动垃圾回收方法管理内存,正常使用的情况下几乎不会发生内存泄露。浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有bug,会产生内存泄露。
二、内存泄露的几种情况
1、当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露。
1 <div id="myDiv">
2 <input type="button" value="Click me" id="myBtn">
3 </div>
4 <script type="text/javascript">
5 var btn = document.getElementById("myBtn");
6 btn.onclick = function(){
7 document.getElementById("myDiv").innerHTML = "Processing...";
8 }
9 </script>
应改成下面
1 <div id="myDiv">
2 <input type="button" value="Click me" id="myBtn">
3 </div>
4 <script type="text/javascript">
5 var btn = document.getElementById("myBtn");
6 btn.onclick = function(){
7 btn.onclick = null;
8 document.getElementById("myDiv").innerHTML = "Processing...";
9 }
10 </script>
或者采用事件委托
1 <div id="myDiv">
2 <input type="button" value="Click me" id="myBtn">
3 </div>
4 <script type="text/javascript">
5 document.onclick = function(event){
6 event = event || window.event;
7 if(event.target.id == "myBtn"){
8 document.getElementById("myDiv").innerHTML = "Processing...";
9 }
10 }
11 </script>
2、
1 var a=document.getElementById("xx");
2 var b=document.getElementById("xxx");
3 a.r=b;
4 b.r=a;
1 var a=document.getElementById("xx");
2 a.r=a;
对于纯粹的 ECMAScript 对象而言,只要没有其他对象引用对象 a、b,也就是说它们只是相互之间的引用,那么仍然会被垃圾收集系统识别并处理。但是,在 Internet Explorer 中,如果循环引用中的任何对象是 DOM 节点或者 ActiveX 对象,垃圾收集系统则不会发现它们之间的循环关系与系统中的其他对象是隔离的并释放它们。最终它们将被保留在内存中,直到浏览器关闭。
3、
1 function bindEvent()
2 {
3 var obj=document.createElement("XXX");
4 obj.onclick=function(){
5 //Even if it's a empty function
6 }
7 }
闭包可以维持函数内局部变量,使其得不到释放。
上例定义事件回调时,由于是函数内定义函数,并且内部函数--事件回调的引用外暴了,形成了闭包
解决之道,将事件处理函数定义在外部,解除闭包
1 function bindEvent()
2 {
3 var obj=document.createElement("XXX");
4 obj.onclick=onclickHandler;
5 }
6 function onclickHandler(){
7 //do something
8 }
或者在定义事件处理函数的外部函数中,删除对dom的引用(题外,《JavaScript权威指南》中介绍过,闭包中,作用域中没用的属性可以删除,以减少内存消耗。)
1 function bindEvent()
2 {
3 var obj=document.createElement("XXX");
4 obj.onclick=function(){
5 //Even if it's a empty function
6 }
7 obj=null;
8 }
4、
1 a = {p: {x: 1}};
2 b = a.p;
3 delete a.p;
执行这段代码之后b.x的值依然是1.由于已经删除的属性引用依然存在,因此在JavaScript的某些实现中,可能因为这种不严谨的代码而造成内存泄露。所以在销毁对象的时候,要遍历属性中属性,依次删除。
5. 自动类型装箱转换
看网上资料,说下面的代码在ie系列中会导致内存泄露,先提个神,具体泄露与否先不管
1 var s=”lalala”;
2 alert(s.length);
s本身是一个string而非object,它没有length属性,所以当访问length时,JS引擎会自动创建一个临时String对象封装s,而这个对象一定会泄露。这个bug匪夷所思,所幸解决起来相当容易,记得所有值类型做.运算之前先显式转换一下:
1 var s="lalala";
2 alert(new String(s).length);
6、某些DOM操作
IE系列的特有问题 简单的来说就是在向不在DOM树上的DOM元素appendChild;IE7中,貌似为了改善内存泄露,IE7采用了极端的解决方案:离开页面时回收所有DOM树上的元素,其它一概不管。