到底怎样才算泄露?

暂且认为:如果在浏览器刷新之前, 内存可以被回收,才算不泄露。

规则1:创建的元素,如果包含内联脚本(这是这条规则的前提情况)一定要挂载在dom树上!不能先挂载到不在树上的元素!

如果不包含内联脚本对象, 则不会泄露。

如果包含了内联脚本,则记得先挂在树上~ 哈哈也就是先把父节点挂载,然后挂载子节点到父节点.

再者说来,在FF等其他浏览器中,根本不允许
document.createElement("<divοnclick='foo();'>"); 这样的代码出现么!
所以按照标准的写法来做是有好处的!

var o=document.createElement(“div”)

o.onclick = foo;

这样就不会有泄露!

 

规则2 不讨论内联事件对象的情况,因为那是个特例, 来看这样的情况

function test(){

       gb =document.getElementById('garbageBin');

       var i = 0;

       while(i++<5000){

             (function(){

             var o =document.createElement("div");

             //产生循环引用!

             o.onclick = function(){

                    alert('haha')

             }

             

             gb.appendChild(o)

             

             //虽然remove了元素,由于循环引用的存在,无法回收内存

             //应当在remove之前,打破循环引用!

             //o.onclick = null; //break!

             gb.removeChild(o)

             

             })();

      }

}

注意哈,这此会导致内存泄露,就像前辈们说的那样, o.onclick = function一句产生了dom元素和function对象的循环引用。就算挂载到dom,然后remove,内存依然不能收回。(似乎能收回一些,但是仍然没有回到调用前的数值), PF使用率成上升趋势.
当然解决方法很简单, 只要在 removeChild 之前将循环打破即可. 使用o.onclick = null 即可,或者事件响应写在函数外面。这个网上有很多讲,不多说。我关注的其实不是这个,主要是, IE7的removeChild到底能做些什么? 刷新页面的时候,回收的又是哪些内存?

(如果不append也不remove,则会彻底的泄露,刷新无济于事.)

我将红色的部分,也就是内部包装的function去掉。循环5000次,内存没有增长!我做这样的猜测:内存泄露是由于循环引用没错,然而onclick 关联的那个function 属于匿名函数哈,在整个过程中只解析一次,因此无论你循环多少次,泄露的都只是跟这一个对象有关,因此泄露数量很少很少,以至于看不出。

然而包装成函数调用5000次呢?就生成了5000个onclick关联的function, 泄露的内容就增大了5000倍,因此我们可以看的出来。

 

3

下面测试正常的创建元素。先测试只创建, 不append到树上的情况.

//在IE7下没有问题,内存不会增长,说明IE7可以动态回收不在结点树上的并且没有关//联JS对象, 且没有其他对象引用它的元素.

function test2(){

       var i = 0;

       while(i++<5000){

             //(function(){

                    var o =document.createElement("<div>");

                    o.innerHTML = "AAA";

                    //加上下面两句也没问题!

                    buf.push(o);//如果后面不做处理,则不会被回收哦

                    buf.pop();   //直接pop掉,也就没有对象引用这个元素了       

 

             //})();

             //buf.pop 放到这里也完全OK,内存不会增长

      }

      

}

 

 

也就是说,定义一个dom元素,只要没有循环引用, 不append到dom树上也没问题。只要没有被引用到,就会被垃圾回收。

这几个例子都是创建一个就删除一个,那么如果先缓存下来,然后再删除呢?

看这个例子:

       var i = 0;

       while(i++<5000){

             

             (function(){

                    var o =document.createElement("<div>");

                    o.onclick = foo;

                    o.innerHTML = "AAA";

                    

                    buf.push(o);//如果后面不做处理,则不会被回收哦

             })();

      }

 

      //直接清空buf

      buf.length = 0;

      /*

      //尝试逐个置空方法

       for(var i=0;i<buf.length;i++){

             //document.body.removeChild(buf[i]);

             buf[i] = null;

      }

      */

      //尝试pop

       //while(buf.pop());

该例子先将5000个对象存在buf里,然后再尝试各种方法去释放他们,也就是断开对他们的引用。然而实验结果是: 占用较大的内存,而且每次调用这个函数,PF使用率都是相同的! 这说明,内存不会累计增加。然而也不会立即的释放。虽然PF使用率数值没变,但是由于再次调用还是占用这些,说明之前占用的内存被新的内容覆盖了。然而总体情况,还是没有得到理想的释放效果。很让人郁闷,为什么创建一个就释放一个就可以,创建多个然后再释放就不完全了呢?

然而这种情况只是在测试,毕竟没人会去创建一堆不挂载到页面dom的元素吧~

 

再来看挂载到dom树上的情况:

下面的例子也OK

while(i++<5000){         

             (function(){

                    var o =document.createElement("<div>");

                    o.onclick = foo; //因为定义在了外部,没有循环引用问题

                    o.innerHTML = "AAA";

                    document.body.appendChild(o);

                    document.body.removeChild(o);

             })();

}

 

如果去掉removeChild一行,内存情况也尚好,只是增加一点,这是正常的,毕竟显示到页面上需要占用内存。然而让我不解的是: 为什么append到页面就占用很少内存,而保存

到buf里面就会占用很大内存呢??不解!
而且,如果先append,然后保存到buf里,占用内存依然很少!
还有如果不设置innerHTML, 则占用内存极少! 里面的原因我就猜不到了.

我只能得出这样的结论: IE中,创建一个元素,务必把它挂载到dom树上!这样即使不remove,刷新后内存也会释放。

 

如果将删除代码移到function外面, 情况一样,没有泄露。

 

再来实验将5000个对象挂载后,然后再删除的情况

我们通过buf保存对象的引用:

       while(i++<10000){

             var o;

             (function(){

                    o = document.createElement("<div>");

                    o.onclick = foo;

                    o.innerHTML = "haha";

                    o.style.left="10px";

                    document.body.appendChild(o);

                    buf.push(o);//保存引用

             })();

      }

       //这里调用removeChild来释放

       while(buf.length>0){

             document.body.removeChild(buf.pop());

      }

测试结果还不错,虽然内存会上升,但是多次调用会维持在一个水平上,这说明,每次调用时,新的对象占用旧的对象的内存,因此不会累计增加,还算OK. 

结论是:同未挂载的情况类似,如果一次性缓存多个对象然后统一removeChild来清除,则IE不会立即释放内存,如果有新的变量或者对象出现,则会覆盖那部分内存。总的情况还不差。

 

使用另一种方式删除:定义一个看不见的元素gb当做垃圾站

      /*

       while(buf.length>0){

             //首先说明,一个元素只能挂载到一个点上,因为dom是一个树结构,

             //元素对象只是将指针挂载到树的某个位置,之前对象在body上,现在挂载到了

             //gb上,那么body中就不显示了,而转到gb上来,

             //另外如果将一个元素innerHTML置空,意味着其子元素的内存被释放!

             //所以,这是一个名副其实的回收站~哈哈

             gb.appendChild(buf.pop());

             gb.innerHTML = "";

             

      }

      */

 

这种情况得到的结果和上面类似,然而内存占用更少,上面是35M左右,而这个维持在25M左右,情况好了不少。只是效率低了一点,因为有append操作

其实如果不做清除的时候,内存占用到了25M. 而清除之后,第一种方法35M,第二种方法25M, 似乎根本没有清除,其实还是这样,新的内容会覆盖旧的内存,只是任务管理器没有显示出来。因为如果不清除,多次调用后内存是累计增加的,而清除后会维持在一个水平。

 

还有pop的速度很慢,改成for会快很多!

var l=buf.length;

       for(var i=0;i<l;i++){

             gb.appendChild(buf[i]);

      }

      gb.innerHTML = ""; //置空,子元素全被释放!

      buf.length = 0;       //重置buf

 

所以,使用这种方法清除元素是再好不过的了。可是需要注意的是,如果内部元素有循环引用的现象,清除之前一定要先把循环引用断开,方法就是递归的清除类型为function的属性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值