JavaScript中DOM节点的循环添加/删除会导致内存泄漏?

6

我正在尝试使用DOM元素来显示动态可变数据(添加/删除它们)。我发现几乎所有的浏览器都有一个非常奇怪的行为:在我删除一个DOM元素然后添加一个新的元素之后,浏览器不会释放被删除DOM项占用的内存。请看下面的代码以了解我的意思。运行此页面后,它将逐步消耗高达150 MB的内存。有人能解释这种奇怪的行为吗?或者我做错了什么吗?

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
  <script type="text/javascript">
     function redrawThings() {
         // Removing all the children from the container
         var cont = document.getElementById("container");
         while ( cont.childNodes.length >= 1 ) {
            cont.removeChild(cont.firstChild);
         }

         // adding 1000 new children to the container
         for (var i = 0; i < 1000; i++) {
             var newDiv = document.createElement('div');
             newDiv.innerHTML = "Preved medved "  + i;
             cont.appendChild(newDiv);             
         }         
     }  
  </script>
  <style type="text/css">
    #container {
        border: 1px solid blue;
    }
  </style>
</head>
<body onload='setInterval("redrawThings()", 200);'>
  <div id="container"> </div>
</body>
</html>

只是一个小提示:不要将字符串作为参数传递给 setInterval 函数:“出于与使用 eval() 相同的原因,不建议使用这种语法。” - Marcel Korpel
谢谢你的留言,Marcel!我不知道那个语法。 - Igor Tkachenko
3个回答

1

我在FF 3.6.8 / Linux上无法重现这个问题,但是对于那么多的DOM重新渲染来说,200毫秒的计时器时间相当短。我注意到在我的机器上,当进行除运行此脚本之外的JavaScript密集型操作(例如在此答案框中键入)时,内存使用量会增加,但在我停止键入时会释放(在我的情况下,释放到大约16%的内存使用量)。

我猜在你的情况下,浏览器的垃圾回收程序没有足够的“空闲时间”来实际从内存中删除这些节点。


我进行了更多的研究,发现我的初步结论是错误的 - 浏览器不会丢失内存,它们会一直占用内存,直到达到一定数量,然后分配的内存就会下降(然后再逐步增长)。例如,Chrome的内存增长到150 MB,然后降至75MB(虽然不是初始的7MB!),IE的内存消耗根本不增长。奇怪的是,在向stackoverflow提问之前,我曾经观察到Chrome、IE和FF的内存消耗稳定增长,但在事情开始按照预期工作之后,这种情况就消失了。非常感谢所有提问的人! - Igor Tkachenko

0

这是因为从DOM树中删除节点并不会将其从内存中删除,它仍然可以访问,因此以下代码将起作用:

var removed = element.removeChild(element.firstChild);
document.body.appendChild(removed);

这段代码将从element中删除第一个子元素,然后在它被删除后,将其附加到文档的末尾。 除了使用更少的删除操作使您的代码更有效率外,您真的无能为力。

有关更多信息,请查看Mozilla开发者中心的Node.removeChild页面。


1
但如果没有分配给任何东西(就像在OP中一样),那么垃圾收集器会从内存中删除它。这不是原因。 - slebetman
不,这就是原因,即使未分配,垃圾回收器也不会将其删除。即使 removed = null,也无法从内存中删除引用,唯一的方法是刷新页面。 - Randy the Dev
是的,我可以确认:垃圾收集器存在,并且它会收集从DOM中删除的项目。但是它在不同的浏览器上的工作方式不同,例如,Chrome的垃圾收集器在我的情况下只有在达到约150MB后才开始工作。 - Igor Tkachenko

0

不确定它是否会影响时间,而且这可能是非常糟糕的做法,但是您可以不通过循环子节点,而是将 div 的 innerHTML 设置为 "" 吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接