Backbone.js 内存管理,上升的 DOM 节点数

Posted

技术标签:

【中文标题】Backbone.js 内存管理,上升的 DOM 节点数【英文标题】:Backbone.js Memory Management, Rising DOM Node Count 【发布时间】:2013-02-14 02:25:11 【问题描述】:

情况:我正在开发一个相当复杂的单页 Backbone 应用程序,它可能会连续运行 8-12 多个小时。因此,需要确保应用程序不会泄漏,并且不会因 X 小时后崩溃或显着减速而闻名。

应用程序:应用程序基于Backbone (mv*)、Zepto(类似于 jquery)、Curl(amd 加载器)和Mustache(模板)构建。

问题:我刚刚征服了事件监听器。垃圾收集器似乎在清理这些家伙方面做得很好,但 DOM 节点数不会停止攀升。

问题

是否有适当的方法来处理 DOM 节点,以便正确地对它们进行垃圾回收,或者这个 DOM 节点计数是一个永远不会减少的运行总数? 是否有人知道这些框架中的任何一个处理 DOM 节点不佳?可能是小胡子? DOM 节点数是一个可靠的数字吗?

我真的只是在寻找阻止这些 DOM 节点上升的冒险的开始。任何帮助或指导将不胜感激(并相应地赞成)。

我假设一旦事件侦听器被正确处理,DOM 节点计数就会自行管理,但情况似乎并非如此。

测试


第一次测试:6.8 分钟,110,000 个 DOM 节点

编辑:在没有时间线记录的情况下,我重新运行相同的脚本以随机混搭链接,并在大约 7 分钟时截取了屏幕截图。 GC通过后我得到了这些结果。

第二次测试:7.1 分钟,141,000 个 DOM 节点(没有时间线记录)

编辑:修复后

Backbone 升级后,到处使用listenTo 和stopListening

7 分钟:6,926 个 DOM 节点(请参阅下面的标记答案)。 20 分钟:6,000 个 DOM 节点、20 个事件侦听器、20 MB 内存。 25 分钟:11,600 个 DOM 节点,44 个监听器,内存 21.7 MB。 28 分钟:9,000 个 DOM 节点,22 个事件监听器,内存 21.7 MB。 30 分钟:13,700 个 DOM 节点,123 个事件监听器,内存 21.7。 31 分钟:7,040 个 DOM 节点,30 个监听器,内存 21.7。

【问题讨论】:

【参考方案1】:

我找到了另一种避免卡顿的方法

render: function() 
  this.$el.empty();
  var container = document.createDocumentFragment();
  // render each subview, appending to our root element
  _.each(this._views, function(subview) 
     container.appendChild(subview.render().el)
  );
  this.$el.append(container);

参考这里http://ozkatz.github.io/avoiding-common-backbonejs-pitfalls.html

【讨论】:

【参考方案2】:

已经解决了! - 升级骨干。 (继续阅读)

我们从 Backbone 0.9.2 升级到 Backbone 0.9.10,并在每个视图/模型/集合上实现了 listenTo 和 stopListening。结果是 OMGFANTASTIC。

运行相同的压力测试 7 分钟后,结果如下:

结果:7.0 分钟,6,926 个 DOM 节点(没有时间线记录),事件侦听器计数看起来像 BLUE BLADES OF GRASS。我感到震惊。与之前的测试相比,内存使用率也低得惊人。

18 分钟后:事件监听器计数相同,从未超过 154,并且 DOM 节点计数始终低于 25,000!显然有些东西漏掉了(很可能一些仍在使用的非主干组件),但改进是惊人的。

结论:在此版本的 Backbone 之前,我们在清理 Backbone 本身内的侦听器方面做得不是很好。 DOM 的侦听器处理得很好,但不是在模型/视图/集合之间。涉及的许多回调都与 Backbone Views 相关联,我猜这会阻止垃圾收集器释放 DOM 节点。 Backbone 中的大量散乱事件侦听器/回调(不仅仅是绑定到 DOM)创建了许多无法被垃圾回收的散乱 DOM 节点。

如果这还不是升级 Backbone 的充分理由,我不知道什么是 ;o

【讨论】:

【参考方案3】:

我假设一旦事件侦听器被正确处理,DOM 节点计数就会自行管理,但情况似乎并非如此。

如果我没听错,您正试图通过从中删除侦听器来处理节点,是这样吗?

请注意,将事件侦听器添加到 DOM 节点并不会阻止该节点被垃圾收集,依赖是相反的方向:当节点处于活动状态时,侦听器函数将不会被收集。

是否有适当的方法来处理 DOM 节点,以便正确地对它们进行垃圾回收,或者这个 DOM 节点计数是一个永远不会减少的运行总数?

为了确保 DOM 节点可以被垃圾回收,你应该

    从文档树中删除节点。 清除从 javascript 到节点的所有引用 AND 到同一子树中的所有节点,因为从 javascript 到子树中某个节点的引用将保留整个子树。

因此,仅从节点中删除侦听器以使其可收集是不够的。此外,如果您希望收集节点,则没有必要从节点中删除侦听器。

当某些节点被 GC 收集并销毁时,DOM 节点数应该会减少。该数字表示当前已创建但未销毁的 DOM 节点数量,因此它不应无限增长,除非存在内存泄漏。

DOM 节点数是一个可靠的数字吗?

是的。它应该是一个可靠的数字,因为每当创建一个新的 DOM 节点时它就会递增,而当它被销毁时它会递减。所以这个实现非常简单,可以信任它。

【讨论】:

谢谢,我会尽快调查此事,并提供进展/结果。【参考方案4】:

上升的 DOM 节点数是内存泄漏的主要标志(通常在我们页面的代码中)。 所以你需要与之抗争。标准技术在this question.的回答中描述

快照内容有太多细节。而这 3 个快照模式可帮助您过滤掉快照中不感兴趣的部分,并仅显示泄漏的候选对象。

请确保您运行的是最新版本的 chrome,例如 Chrome Canary。 它应该是一个没有扩展名的带有单个选项卡的新实例。 最好在控制台中没有错误消息,没有断点并且不要在异常上停止,因为所有这些都可能影响页面并因此影响快照内容。

This post 可能对你来说也很有趣。

【讨论】:

谢谢,我会尽快调查一下,然后将我的结果回复给您。目前正在运行压力测试,我的 DOM 节点数 > 180 万,而事件侦听器数保持在 40-450 的范围内,但通常在 40 到 275 之间。 其实不用等那么久。如果页面有泄漏,它肯定会使渲染器进程因 OOM 崩溃。 您可以使用 3-snapshot 技术并在 5 分钟内获取泄漏对象的列表,或者等待一个小时并在单个快照中查找最大的内存消耗。

以上是关于Backbone.js 内存管理,上升的 DOM 节点数的主要内容,如果未能解决你的问题,请参考以下文章

backbone.js1.3.3------------------------view

Backbone.js 状态管理/基于 url 片段的视图初始化

Backbone.js 模型和集合的最佳实践

Backbone.js:组合多个模型的复杂视图

寻找有关在Backbone.js中使用Parceljs的建议

带有木偶或卓别林的 jQuery Mobile?