javascript内存泄露

来源:互联网 发布:windows develop 编辑:程序博客网 时间:2024/05/29 10:23

原文: http://www.pc6.com/infoview/Article_48896.html


以下是常见的几种javascript内存泄露的情况:

一、循环引用:

  1.   <html>
  2.      <head>
  3.          < script language ="JScript">
  4.          var  myGlobalObject;
  5.          function  SetupLeak()  // 产生循环引用,因此会造成内存泄露
  6.         {
  7.              //  First set up the script scope to element reference
  8.             myGlobalObject  = document.getElementById("LeakedDiv");
  9.              //  Next set up the element to script scope reference
  10.             document.getElementById("LeakedDiv").expandoProperty  =  myGlobalObject;
  11.         }
  12.          
  13.      </head>
  14.      <body onload = "SetupLeak()">
  15.          <div id ="LeakedDiv" ></div>
  16.      </body>
  17.  </html>

我们可以看到,myGlobalObject指向了一个DOM对象,而这个DOM对象的一个属性又指向了myGlobalObject,循环引用出现,内存泄露,其原理如下:

解决方案很简单,在确保属性不再使用后,加入以下代码就可以了:

  1. function  BreakLeak(){  // 解开循环引用,解决内存泄露问题
  2.           document.getElementById( " LeakedDiv " ).expandoProperty  =  null ;
  3. }

说起来容易,不过当我们程序非常复杂的时候,发现和修改就没有这么容易了.

二、闭包(Closures)

仍然先看一段代码:

  1. <html>
  2.      <head>
  3.          <script language="JScript">
  4.          function  AttachEvents(element)
  5.         {
  6.              //  This structure causes element to ref ClickEventHandler  
  7.             element.attachEvent( " onclick " , ClickEventHandler); function  ClickEventHandler()
  8.             {
  9.                  //  This closure refs element  
  10.                
  11.             }
  12.         } function  SetupLeak()
  13.         {
  14.              //  The leak happens all at once
  15.             AttachEvents(document.getElementById( " LeakedDiv " ));
  16.         }
  17.         </script>
  18.      </head> <body onload="SetupLeak()">
  19.          <div id="LeakedDiv"></div>
  20.      </body>
  21. </html>

闭包的一个内部方法赋给了element对象,产生了一个作用域的循环引用,从而造成内存泄露.其原理图如下:

 

解决方案如下,在确定事件不再使用后,解除事件的绑定:

  1. function BreakLeak() {
  2.      document.getElementById(”LeakedDiv”).detachEvent(”onclick”, document.getElementById(”LeakedDiv”).expandoClick);  
  3.      document.getElementById(”LeakedDiv”).expandoClick = null;
  4. }

通常情况下,常用的js框架都帮我们解决了这个问题,不需要我们自己处理,这也是使用框架的一个好处.

三、Cross-Page-Leaks

仍然先看一个例子:

  1. <html>
  2.      <head>
  3.          <script language="JScript">
  4.          function  LeakMemory()  
  5.         {
  6.              var  hostElement  =  document.getElementById("hostElement"); //  Do it a lot, look at Task Manager for memory response
  7.  
  8.              for (i  =   0 ; i  < 5000 ; i ++ )
  9.             {
  10.                  var  parentDiv  =
  11.                     document.createElement("<div onClick='foo()'>");
  12.                  var  childDiv  =
  13.                     document.createElement("<div onClick='foo()'>"); //  This will leak a temporary object
  14.                 parentDiv.appendChild(childDiv);
  15.                 hostElement.appendChild(parentDiv);
  16.                 hostElement.removeChild(parentDiv);
  17.                 parentDiv.removeChild(childDiv);
  18.                 parentDiv  =   null ;
  19.                 childDiv  =   null ;
  20.             }
  21.             hostElement  =   null ;
  22.         } function  CleanMemory()  
  23.         {
  24.              var  hostElement  =  document.getElementById("hostElement"); //  Do it a lot, look at Task Manager for memory response
  25.  
  26.              for (i  =   0 ; i  < 5000 ; i ++ )
  27.             {
  28.                  var  parentDiv  =   document.createElement("<div onClick='foo()'>");
  29.                  var  childDiv  =   document.createElement("<div onClick='foo()'>"); //  Changing the order is important, this won’t leak
  30.                 hostElement.appendChild(parentDiv);
  31.                 parentDiv.appendChild(childDiv);
  32.                 hostElement.removeChild(parentDiv);
  33.                 parentDiv.removeChild(childDiv);
  34.                 parentDiv  =   null ;
  35.                 childDiv  =   null ;
  36.             }
  37.             hostElement  =   null ;
  38.         }
  39.          </div></div></script>
  40.      </head>
  41.      <body>
  42.          <button onclick ="LeakMemory()"> Memory Leaking Insert </button>
  43.          <button onclick ="CleanMemory()"> Clean Insert </button>
  44.          <div id ="hostElement"></div>
  45.      </body>
  46. </html>

LeakMemory和CleanMemory这两段函数的唯一区别就在于他们的代码的循序,从代码上看,两段代码的逻辑都没有错.

但LeakMemory却会造成泄露.原因是LeakMemory()会先建立起parentDiv和childDiv之间的连接,这时候,为了让 childDiv能够获知parentDiv的信息,因此IE需要先建立一个临时的scope对象.而后parentDiv建立了和 hostElement对象的联系,parentDiv和childDiv直接使用页面document的scope.可惜的是,IE不会释放刚才那个临时的scope对象的内存空间,直到我们跳转页面,这块空间才能被释放.而CleanMemory函数不同,他先把parentDiv和 hostElement建立联系,而后再把childDiv和parentDiv建立联系,这个过程不需要单独建立临时的scope,只要直接使用页面 document的scope就可以了, 所以也就不会造成内存泄露了.但是,需要特别说明一下,如果LeakMemory方法里面,创建的div对象上不绑定script事件,那么也不会有泄漏,这个可以理解为ie的bug,大家记住就可以了,不需要过分深究.其原理如下:

四、Pseudo-Leaks:

同样可以理解为ie的bug的一种泄露:

  1. <html>
  2.     <head>
  3.         <script language="JScript">
  4.  
  5.         function LeakMemory()
  6.         {
  7.             // Do it a lot, look at Task Manager for memory response
  8.  
  9.             for(i = 0; i < 5000; i++)
  10.             {
  11.                 hostElement.text = “function foo() { }”;
  12.             }
  13.         }
  14.         </script>
  15.     </script></head>
  16.  
  17.     <body>
  18.         <button onclick=”LeakMemory()”>Memory Leaking Insert</button>
  19.         <script id=”hostElement”>function foo() { }</script>
  20.     </body>
  21. </html>

没什么特别的好解释,记住就可以了.

关于这四种泄漏的具体描述,还是请各位参照原文:http://msdn.microsoft.com/en-us/library/Bb250448

以上是几种主要的泄露,当然,除此之外,网上还有一些其他的讨论,比如var str = "lalala";alert(str.length);这个简单的语句也会造成内存泄露,原因是类型转换的时候,ie生成了一个临时对象,这个临时对象被泄漏了.类似情况还有很多,大家有兴趣可以自己去搜集整理.

最后说一下,只要ie6还健在,作为前端开发人员,就不能逃避这些问题,当然,也不必过分深究,比如闭包的情况就比较难避免,就像我一开始说的,毕竟,javascript造成的内存泄露不是程序和项目的瓶颈,我们需要在各方面进行权衡.


0 0
原创粉丝点击