42

我正在使用 chrome 开发工具来确定某些 JS 代码中是否存在内存泄漏。内存时间线看起来不错,内存按预期回收。

在此处输入图像描述

但是,内存快照令人困惑,因为“分离的 DOM 树”下存在条目,因此看起来好像存在泄漏。

“分离的 DOM 树”下的东西是等待垃圾收集还是这些真正的泄漏?

还有人知道如何找出对分离元素的引用是什么函数吗?

在此处输入图像描述

4

3 回答 3

34

这些元素在您的代码中被引用,但它们与页面的主 DOM 树断开连接。

简单的例子:

var a = document.createElement("div");

aa现在引用一个断开连接的元素,当它仍在范围内时,它不能被 GC'd 。

如果分离的 dom 树在内存中持续存在,那么您将保留对它们的引用。使用 jQuery 做到这一点有点容易,只需保存对遍历结果的引用并保留它。例如:

var parents = $("span").parent("div");
$("span").remove();

现在跨度被引用,即使看起来你无论如何都没有引用它们。通过 jQuery属性parents间接保持对所有跨度的引用。.prevObject这样做parents.prevObject会给出引用所有跨度的对象。

请参阅此处的示例http://jsfiddle.net/C5xCR/6/。尽管没有直接显示跨度会被引用,但它们实际上是由parents全局变量引用的,您可以看到 Detached DOM 树中的 1000 个跨度永远不会消失。

现在这是相同的 jsfiddle,但具有:

delete parents.prevObject

你可以看到跨度不再在分离的 dom 树中,或者任何地方。http://jsfiddle.net/C5xCR/7/

于 2012-08-13T08:01:49.583 回答
16

“分离的 DOM 树”下的东西是等待垃圾收集还是这些真正的泄漏?

在拍摄快照之前,浏览器将运行垃圾收集并清除所有未引用的对象。所以堆快照总是只包含活动对象。因此,如果快照中存在分离的 DOM 树,则树中必须有一个从 JavaScript 引用的元素。

还有人知道如何找出对分离元素的引用是什么函数吗?

在同一个分离的 DOM 树中应该有一个元素(或其中几个)具有黄色背景。这些元素是从 JavaScript 代码中引用的。您可以找出谁确切地保留了对保留树中元素的引用。

于 2012-08-14T13:15:39.097 回答
1

既然你已经添加了 jQuery 标签,我就偷偷地怀疑这是一个 jQuery的东西。一个快速的谷歌把我带到了这个页面。使用 jQ 的detach方法时,对对象的引用仍保存在内存中,因此可能会导致您的快照。

另一件事可能是 jQuerydiv手头有一个节点,该节点 - 显然 - 保存在内存中,但从未添加到实际的 dom 中......有点document.createNode('div')不附加它。这也将显示在内存快照中。你无法解决这个问题,jQuery 使用它将字符串解析为 html 元素。

因此,要从内存中删除一些元素,请使用 jQuery.remove()方法,您的内存将立即被清除cf Esailija 关于为什么remove不完全符合这里的要求的评论。
$('#someElem')[0].parentNode.removeChild($('#someElem')[0]);应该完全删除元素,但可能不会取消绑定事件。也许是这样的:

$('#someElem').detach();//to remove any event listeners
$('#someElem')[0].parentNode.removeChild($('#someElem')[0]);//remove element all together

而且,正如 Esailija 在他的回答中指出的那样,确保将对任何 jQuery 对象 ( var someRef= $('.someSelector');) 的引用分配给全局变量,因为它们不会被 GC 处理。事实上,只要避免全局变量。
但是要简短而清楚地回答您的问题:不,这些不是真正的内存泄漏,应该在onbeforeunload事件中释放内存。jQuery 对象被删除,因此所有引用都超出了范围。至少,这就是我的“研究”让我相信的。也许不完全相关,但只是作为参考这是我不久前发布的关于 mem-leaks 的问题,并通过它发现了一些事情..

于 2012-08-13T08:10:12.177 回答