【问题标题】:Memory leak with d3.js keyed join in event handler closure在事件处理程序关闭中使用 d3.js 键控连接的内存泄漏
【发布时间】:2015-03-07 23:34:32
【问题描述】:

如果事件处理闭包引用使用键控数据连接的 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

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

【问题讨论】:

  • 注:我发现这是一个逻辑错误的一部分,我通过避免关闭引用来修复它。我不提倡这种模式(因为它可能会导致内存泄漏),但我不了解这种特定情况下的行为,因此提出了问题。

标签: javascript d3.js memory-leaks


【解决方案1】:

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

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

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

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

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

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

将摆脱对您的 clickHandler 的引用。

【讨论】:

【解决方案2】:

很有趣。我发现了由 d3.transition() 引起的内存泄漏。基本上发生的事情是图表在一段时间不活动后挂起 - 首先更新停止,然后整个选项卡崩溃,图表完全消失,而任务管理器显示出巨大的内存使用量,增长缓慢。删除过渡解决了所有提到的问题。

【讨论】:

    猜你喜欢
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 2012-10-29
    • 2012-05-31
    • 1970-01-01
    • 2011-10-30
    • 2011-07-29
    • 1970-01-01
    相关资源
    最近更新 更多