JavaScript内存泄漏:分离的DOM树

33

我注意到当我在表单中时,浏览器的内存开始增加(从任务管理器中可以看出)。在IE 9中,经过一段时间后,内存轻松地超过500MB,而使用相同测试的chrome更具弹性(最多使用200MB)。

我正在使用Chrome开发者工具调试此问题。我注意到有大量的Detached DOM tree:

detached dom tree screenshot

我假设这可以确认存在内存泄漏。那正确吗? 其次,我需要找出如何识别问题的根本原因。我知道您应该使用保留树来确定是什么阻止了这些项目被回收利用。但我找不到如何使用保留树。例如,上面截图中的保留树是什么意思?

任何帮助都将不胜感激。


2
你能提供一些代码来阐明“当我在表单中时,浏览器的内存开始增加”的问题吗?这有助于找到内存泄漏。 - Ikrom
这个其他问题/答案有帮助吗? - msung
2个回答

27

当您编写引用DOM元素的代码时,需要考虑许多因素。但基本上都可以归结为几个简单的要点 -

a. 在本地函数中,始终清除引用

var menu = $('body #menu');
// do something with menu
 .
 .
 .
 menu = null;
b. 不要将引用作为元素数据的一部分存储在 .data() c. 尽量避免在闭包/内联处理程序中使用DOM引用,而是传递标识符
    function attachClick(){
      var someDiv = $('#someDiv');

      someDiv.click(function(){
         var a = someDiv....;
         //Wrong. Instead of doing this..
      });


      someDiv.click(function(){
         var a = $('#someDiv');
         //Pass the identifier/selector and then use it to find the element
      });       


      var myFunc = function(){
         var a = someDiv;
         //using a variable from outside scope here - big DON'T!             
      }
    }

是的,有人会认为搜索元素会减缓页面速度,但与大堆栈在大型单页应用程序中造成的性能损失相比,延迟非常小。因此,在权衡利弊之后应仅使用#3。(在我的案例中确实有很大帮助)

更新

d. 避免使用匿名函数 - 为您的事件处理程序和本地函数命名将在分析/查看堆快照时对您有所帮助。


2
@pebbl:我检查了我的代码。你是对的。我指的是闭包而不仅仅是将它们传递给方法。已更正上面的示例。 - Robin Maben
1
@AlexMcMillan:如果您不介意传递对DOM对象的引用,那么可以放心使用它!在原始值/对象的情况下,这并不是什么大问题。 - Robin Maben
2
我不同意这些提示。 (a) 是不必要的;只要没有剩余引用,每当范围离开(例如当您离开本地函数)时,它就会被清除。 (b) 如果您负责存储引用的数量,则可以使用它们,无论您在何处存储它们都无所谓。 (c) 同样的道理。只要您负责存储引用的数量即可。从更高的范围使用变量是可以的,并且可以提高性能。更大的问题是,您可能正在创建元素并将其分离,而不是实际删除它们。 - ZachB
2
请注意,还有附加到DOM元素的事件监听器,当DOM元素被移除时没有被删除。 - fadomire
1
@RobinMaben 快问,在例子c中,你的例子很有道理,但是这不会暗示最好不要引用变量el来添加点击处理程序吗? - aug
显示剩余3条评论

2
看起来您的代码创建了许多DOM子树,并从javascript中保留对它的引用。您需要从分离的DOM树中选择一个元素。根据快照,您应该选择文本元素。并查看保留器树。
此树向您显示使对象保持活动状态的所有路径。至少有一条路径(通常是最短的路径)会将您带到窗口对象。如果您熟悉代码,则可以轻松找到该路径中必须删除但未删除的对象。在该路径中可能有许多这样的对象。距离最小的对象更有趣。

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