【问题标题】:Is this a Javascript memory leak?这是 Javascript 内存泄漏吗?
【发布时间】:2015-05-04 17:13:02
【问题描述】:

我已经在 Chrome 开发者工具中分析了我的 web 应用程序,并提出了上面显示的时间线。我正在创建和删除悬停功能的元素。我在this article 中读到过这个。

这种模式是否表明我有内存泄漏?节点数(绿线)持续上升,内部 GC 没有下降。但是在我的强制 GC 上下降到零。这是常见的行为吗?

我的意思是,它在内存中拥有很多节点,即使它们不存在。如果我检查 heap,不存在对 DOM 节点的引用(没有分离的 DOM 等),这让我认为这不是内存泄漏?

你的五美分是多少?

代码如下:

$(document).on("mouseenter", ".btn", function(e){
    var el = document.createElement("div");
    el.id = "box";
    document.body.appendChild(el);
});
$(document).on("mouseleave", ".btn", function(e){
    $("#box").remove();
});

【问题讨论】:

  • 如果它在强制 GC 上下降到 0,那么就没有泄漏。

标签: javascript jquery html memory memory-leaks


【解决方案1】:

在您从 Chrome 显示的跟踪中,我没有看到内存泄漏。您缺少的信息是当垃圾收集周期开始时,该周期可能部分收集垃圾。让我解释一下。

我不熟悉 v8 中的垃圾收集器,但过去我曾多次研究过垃圾收集器。垃圾收集是快速释放未使用内存和应用程序响应之间的平衡行为。特别是在交互式应用程序中,您不希望长时间暂停执行以允许完整的垃圾回收周期,因为这会影响用户体验。因此,存在垃圾收集周期可以是部分而不是完整的策略。看来v8使用了这样的策略,因为Google's blurb on v8's garbage collection声明:

这意味着 V8:

  • 在执行垃圾回收周期时停止程序执行。
  • 在大多数垃圾回收周期中只处理部分对象堆。这将停止应用程序的影响降至最低。

因此,您不应期望大多数循环会将节点计数降至零。

为什么强制 GC 会将计数降至零?通过从 Google 关于调试内存泄漏的文档中推断,我推断强制 GC 循环不仅会强制开始 GC 循环,而且强制循环完成而不是部分,否则强制循环将是对于想知道是否存在内存泄漏的人来说毫无用处。

【讨论】:

  • 谢谢先生,原来如此。结果是垃圾收集了一些节点并在内存中留下了一些。完全强制 GC 会清除所有内容,这反过来意味着没有内存泄漏。 200便士给格兰芬多!
【解决方案2】:

好的,让我们试着面对这个问题。

可能性:

Javascript 内存管理是如何工作的?一切都取决于可达性:

  1. 假定一组有区别的对象是可访问的:它们是 称为根。通常,这些包括所有对象 从调用堆栈中的任何位置引用(即,所有本地 当前正在调用的函数中的变量和参数), 和任何全局变量。
  2. 对象保存在内存中 通过引用或引用链从根访问。

http://javascript.info/tutorial/memory-leaks

所以基本上,当对象无法访问时,JS 会从内存中删除它。让我们尝试一个例子:

HTML:

<html>
<div class="ourDiv"></div>
</html>

JS:

$(".ourDiv").append(document.createElement("span"))
            .remove();

在这个例子中,我故意使用类而不是 ID,你会明白为什么。我们的记忆应该是这样的:

  1. 我们在内存中有 html、body 和 div 标签
  2. 我们将 span 附加到 ourDiv,因此我们在内存中有 html、body、div 和 span 标签
  3. 删除 ourDiv 后,span 元素无法访问,因此在内存中我们有:html 和 body 标签

让我们将其修改为更类似于您的代码:

JS:

var newEl=document.createElement("span");
newEl.id = "ourSpan";
$(".ourDiv").append(newEl)
            .remove();

现在怎么样了?

元素#id 是一个例外。它可以作为#ourSpan 访问,所以它 留在记忆中。当然如果你检查它的parentNode,它会是 空。

  1. 我们在内存中有 html、body 和 div 标签
  2. 我们将带有 ID 的 span 附加到 ourDiv,因此我们在内存中有 html、body、div 和 span 标签
  3. 删除 ourDiv 后,span 元素仍然可以访问,因为它设置了 ID,因此在内存中我们有:html、body 和 span

结论:

当您为每个创建的元素设置 ID 时,即使您尝试使用 jQuery 调用删除它们,javascript 也会将其保存在内存中。可能是因为将创建元素与本机 API 混合并在 jQuery 中将其删除。尝试检查您的 $.cache 大小 - 如果它太大,则意味着 jQuery 没有正确删除对象。

但首先 - 避免对不断创建的元素使用 ID 应该会有所帮助。

更多解释见:Chrome Javascript docs,Firefox Javascript Docs

【讨论】:

    【解决方案3】:

    对此不是 100% 确定,但我认为问题可能是您的 mouseenter/mouseleave 事件没有解除绑定?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-20
      • 2013-08-12
      • 2013-01-08
      • 2013-11-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多