javascript - 事件处理程序关闭中 d3.js 键控连接的内存泄漏

标签 javascript d3.js memory-leaks

如果事件处理闭包引用使用键控数据连接的 d3.js 选择,它们似乎会导致 DOM 节点泄漏。

为什么会这样?是 d3.js 的问题还是调用它的方式的问题?

此示例在重复调用 step 时泄漏 HTMLLIElement 对象(不必执行 clickHandler):

function getKeys(n) {
  // returns a random array of n unique Strings, e.g. ["Alpha", "Quebec", "Charlie"]
}

function step() {

  function clickHandler() {
    // removing this reference removes the leak
    // (note that the outer variable is pulled into closure scope regardless of whether this function is called).
    listItems;
  }

  var keys = getKeys(3);

  var listItems = d3.selectAll('li')
    .data(keys,  function(d) { return d }); 

  listItems.enter()
    .append('li')
    .text(function(d) { return '#' + d })
    .on('click', clickHandler)

  listItems.exit()
    .remove()
}

JSBin

DevTools-friendly version

此模式可通过 D3.js 3.5.3 重现并可在 Chrome 39 中识别。

当满足两个条件时,DOM 节点似乎会泄漏:

  1. 选择有关键作用
  2. 一个闭包,用作选择中的一个节点的事件处理程序,具有对外部范围选择的引用。不必执行闭包。

这些步骤中的任何一个都可以防止内存泄漏:

  • 在调用 data 时不使用键函数
  • step 的末尾添加 listItems = null
  • 避免在闭包中引用外部选择
  • 在点击处理闭包中添加 listItems = null

后一点特别有趣,因为它释放了所有 泄漏的节点,而不仅仅是当前listItems 选择中的节点。这意味着选择是链接的,这是我没想到的。

检查 Chrome DevTools 中的堆快照显示泄漏的 HTMLLIElement 对象在其保留器层次结构中有两个不同的 listItems:

DevTools screenshot of leaked nodes

这是预期的行为吗?如果是这样,是什么原因造成的?这是我的代码或 d3.js 中的内存泄漏吗?

最佳答案

在添加新元素的进入阶段,您将绑定(bind)到每个新添加的“li”元素的 onClick 处理程序。

listItems.enter()
  .append('li')
  .text(function(d) { return '#' + d })
  .on('click', clickHandler);

在退出阶段,您将删除不再需要的“li”元素。但是,在删除“li”元素之前,您并未解除与 onClick 处理程序的绑定(bind)。

在您发布的分析器图像中,请注意 HTMLLIElement 是红色的。 Chrome 的内存分析器告诉您 HTMLIElement 已与 DOM 树断开连接,但仍有对它的 javascript 引用。在这种情况下,“li”元素的 onClick 处理程序引用了您的 js 代码。

在 D3 的退出阶段通过调用 .on('click',null) 移除点击处理程序。

listItems.exit()
  .on('click', null)
  .remove();

将删除对您的 clickHandler 的引用。

关于javascript - 事件处理程序关闭中 d3.js 键控连接的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27864091/

相关文章:

java - 拍照时数据库泄漏。 SQLiteDatabase 创建且从未关闭。安卓、Java、SQLite

javascript - Jquery 每个在 ajax $.post 之后都不继续

javascript - 简单编辑器

javascript - jQuery:将 <section> 的高度设置为其所有子项的高度?

javascript - 如何使用渐进增强技术来渐进加载缓慢的页面?

c - 使用 valgrind 找出对象如何仍可访问

d3.js - 数据可视化初学者 : Learn processing, d3.js 还是其他先?

javascript - AngularJs 选择图表框架 d3.js 与 highcharts

javascript - 为散点图中的每个点绘制词云

.net - WPF 中的 WinForms WebBrowser 内存泄漏