引:http://www.javaeye.com/topic/180611
在ie的内存泄露中跨页面的泄露是最严重的,浏览器刷新了仍然无法释放掉泄露占用的资源,造成访问速度越来越慢,内存占用越来越大
closure引起cross page leak的主要原因是closure和dom元素的互相引用看这个例子:
Java代码
- <div id="bb"><div id="aa">cc</div></div>
- <script type="text/javascript">
- function leakTest(){
- var a=[];//用来加大闭包资源占用,方便观察
- for(var i=0;i<100000;i++){
- a.push('a');
- }
- var divA=document.getElementById('aa');
- divA.kk=function(){};
- divA.parentNode.removeChild(divA);
- }
- leakTest();
- </script>
<div id="bb"><div id="aa">cc</div></div> <script type="text/javascript"> function leakTest(){ var a=[];//用来加大闭包资源占用,方便观察 for(var i=0;i<100000;i++){ a.push('a'); } var divA=document.getElementById('aa'); divA.kk=function(){}; divA.parentNode.removeChild(divA); } leakTest(); </script>
用sIEve看下发现这个页面每次刷新都会产生跨页面泄露,ie内存占用大了7MB,具体fins的文章中有过介绍
在这个例子中我们在leakTest()中创建了一个内部匿名函数并在dom元素aa的自定义属性kk中保存了他的引用,这就产生了一个闭包
divA.parentNode.removeChild(divA);
这句是产生泄露的主要原因,移除了aa并不会使这个节点消失,只不过在dom树上无法访问这个节点而已,由于闭包的存在divA这个局部变量不会 被释放,而divA中保存着aa的引用,这就形成了一个循环引用,闭包保存了dom元素aa的引用,dom元素aa的自定义属性kk又保存了闭包内部的匿 名函数的引用,所以在页面刷新的时候IE无法释放掉这个aa和闭包的资源,在我这个例子中就比较吓人,刷一下涨几MB内存
让我们删掉divA.parentNode.removeChild(divA);这句试试,发现没有泄露发生
我推测IE在刷新时会强行释放掉dom树上的元素,而不存在于dom树中的节点不会强行释放,所以造成了跨页面泄露,这是我的个人推测,有别的意见欢迎讨论
怎么解决这个问题呢,其实我们只要打断引用链就行了
Java代码
- <div id="bb"><div id="aa">cc</div></div>
- <script type="text/javascript">
- function leakTest(){
- var a=[];
- for(var i=0;i<100000;i++){
- a.push('a');
- }
- var divA=document.getElementById('aa');
- divA.kk=function(){};
- divA.parentNode.removeChild(divA);
- divA=null;
- }
- leakTest();
- </script>
<div id="bb"><div id="aa">cc</div></div> <script type="text/javascript"> function leakTest(){ var a=[]; for(var i=0;i<100000;i++){ a.push('a'); } var divA=document.getElementById('aa'); divA.kk=function(){}; divA.parentNode.removeChild(divA); divA=null; } leakTest(); </script>
或者
Java代码
- <div id="bb"><div id="aa">cc</div></div>
- <script type="text/javascript">
- function leakTest(){
- var a=[];
- for(var i=0;i<100000;i++){
- a.push('a');
- }
- document.getElementById('aa').kk=function(){};
- document.getElementById('aa').parentNode.removeChild(document.getElementById('aa'));
- //这个例子不保存aa的应用,也不会引起泄露
- }
- leakTest();
- </script>
<div id="bb"><div id="aa">cc</div></div> <script type="text/javascript"> function leakTest(){ var a=[]; for(var i=0;i<100000;i++){ a.push('a'); } document.getElementById('aa').kk=function(){}; document.getElementById('aa').parentNode.removeChild(document.getElementById('aa')); //这个例子不保存aa的应用,也不会引起泄露 } leakTest(); </script>
or
Java代码
- <div id="bb"><div id="aa">cc</div></div>
- <script type="text/javascript">
- function leakTest(){
- var a=[];
- for(var i=0;i<100000;i++){
- a.push('a');
- }
- var divA=document.getElementById('aa');
- divA.kk=function(){};
- divA.parentNode.removeChild(divA);
- return divA;
- }
- var divA=leakTest();
- divA.kk=null; //这个可以看到内存占用比上面少了7MB,因为解除了对闭包内部函数的引用,闭包占用的资源被释放了
- </script>
<div id="bb"><div id="aa">cc</div></div> <script type="text/javascript"> function leakTest(){ var a=[]; for(var i=0;i<100000;i++){ a.push('a'); } var divA=document.getElementById('aa'); divA.kk=function(){}; divA.parentNode.removeChild(divA); return divA; } var divA=leakTest(); divA.kk=null; //这个可以看到内存占用比上面少了7MB,因为解除了对闭包内部函数的引用,闭包占用的资源被释放了 </script>
通过上面的例子可以看出,如果某个函数中dom元素保存了内部函数的引用,就会形成闭包,很容易引起泄露,务必小心
另firefox下测试是没有这些问题的