javascript,循环引用和内存泄漏

jld*_*ont 24 javascript browser memory-leaks circular-reference

从我记得的一个不太遥远的过去,Javascript解释器在面对循环引用时遭遇了内存泄漏问题.

在最新的浏览器中仍然如此吗?(例如Chrome,FF 3.5等)

bob*_*nce 28

当您在JavaScript对象和DOM节点之类的宿主对象之间建立引用循环时,我们与JavaScript讨论的绝大多数泄漏都特别在IE6-7中.

在IE6中,这是特别有害的,因为当你离开页面时你没有得到回忆; 它一直消失,直到你退出浏览器.在IE7中清除页面现在会返回内存,但是当你有一个长时间运行的应用程序时仍然会遇到困难.IE8通过将DOM节点转换为本机JavaScript对象而不是主机对象来正确解决大部分问题.(您仍然可以通过在参考循环中包含其他非本机对象(如ActiveX对象)来触发IE8中的泄漏.)

对于所有浏览器,随机位置肯定会出现小的模糊内存泄漏,特别是在旧版本中.但是没有一种方法可以像IE refloop问题那样轻松地分类和避免它们.


Mar*_*aio 17

为了增加bobince的答案,我用IE8做了一些测试.

我尝试了http://www.javascriptkit.com/javatutors/closuresleak/index.shtml提供的几乎所有示例

他们中的任何一个都不再泄漏内存(至少不是以可感知的方式),除了删除仍然附加有事件的子节点的示例.

这种类型的例子我认为道格拉斯克罗克福德在他的队列测试2中更好地解释了这个例子.

这个仍然在IE8上泄漏内存,只需运行测试脚本并查看Windows任务管理器 - 性能 - PF使用情况就可以轻松测试.您将看到每次循环PF使用量增加近1MB(非常快).

但是在IE8中,内存在页面卸载时释放(比如导航到新页面重新加载同一页面),显然也是在完全关闭浏览器时.

因此,为了让最终用户能够感知IE8上的内存泄漏(因为系统性能降低),他需要长时间保持在同一页面上,这在现在可能经常发生在AJAX中,但此页面还需要做几百个子节点删除附加事件的元素.

道格拉斯克罗克福德测试强调浏览器添加10000个节点然后删除,这非常适合向您展示问题,但在现实生活中,我从未有过删除超过10个元素的页面.INMHO通常使用速度更快,display: none而不是删除整个节点集,这就是为什么我不使用removeChild那么多.


对于那些可能对上面解释的IE8内存泄漏更感兴趣的人,我做了另一个测试,当使用innerHTML代替appendChild/removeChild添加/删除附加事件的子元素时,IE8中似乎没有显示内存泄漏.所以显然道格拉斯·克罗克福德的清除功能(由他建议防止IE中的内存泄漏)在IE8中不再需要了,至少在使用时innerHTML......

(感谢下面的4esn0k评论)...而且Douglas Crockford 清除函数在IE8上根本不起作用,在他的代码中var a = d.attributes返回在IE8上运行时添加的NO onclick属性(或任何其他onevent属性)(它们确实在IE7上返回).

道格拉斯·克罗克福德说:

"在删除任何元素之前,应该通过removeChild方法或通过设置innerHTML属性来调用清除函数."

我在这里提供测试代码:

<body>    
   <p>queuetest2 similar to the one provided by Douglas Crockford
   at http://www.crockford.com/javascript/memory/leak.html
   <br>but this one adds/removes spans using innerHTML
   instead of appendChild/removeChild.</p>

   <div id="test"></div>    
   <script>
       /* ORIGINAL queuetest2 CODE FROM DOUGLAS CROCKFORD IS HERE
          http://www.crockford.com/javascript/memory/queuetest2.html */

      (function (limit, delay) 
      {
          var n = 0;
          var add = true;

          function makeSpan(n) 
          {
              var div = document.getElementById('test');
              //adding also an inline event to stress more the browser
              div.innerHTML = "<span onmouseover=\"this.style.color = '#00ff00';\">" + n + "</span>";
              var s = div.getElementsByTagName('span')[0];
              s.onclick = function(e) 
              {
                  s.style.backgroundColor = 'red';
                  alert(n);
              };
              return s;
          }

          function process(n) 
          {
              if(add)                    
                 s = makeSpan(n);
              else
                 s.parentNode.innerHTML = ""; //removing span by clearing the div innerHTML
              add = !add;
          }

          function loop() 
          {
              if (n < limit) 
              {
                  process(n);
                  n += 1;
                  setTimeout(loop, delay);
              }
          }

          loop();
      })(10000, 10);

   </script>
</body>
Run Code Online (Sandbox Code Playgroud)