重复应用 d3 转换导致的内存泄漏

Memory leak from repeatedly applying d3 transition

本文关键字:内存 泄漏 转换 应用 d3      更新时间:2023-09-26

我有一个SVG地图和一个间隔,用于轮询数据更改并相应地更新地图上的颜色。 除非我使用过渡淡入新颜色,否则这一切都可以正常工作。 然后选项卡慢慢占用越来越多的内存,直到崩溃。

我做了一个简化的例子,显示了相同的行为:

var size = 500;
var num = 25;
var boxSize = size / num;
function color(d) {
    return '#' + Math.random().toString(16).slice(2,8);
}
var svg = d3.select('body')
    .append("svg")
    .attr("width", size)
    .attr("height", size);
var squares = svg.selectAll(".square")
    .data(d3.range(num * num))
  .enter().append("rect")
    .attr("class", "square")
    .attr("width", boxSize)
    .attr("height", boxSize)
    .attr("x", function (d) { return boxSize * (d % num);})
    .attr("y", function (d) { return boxSize * Math.floor(d / num); })
    .style("fill", color);
function shuffleColors() {
    squares.interrupt().transition().duration(500).style("fill", color);
    timer = setTimeout(shuffleColors, 1000);
}
var timer = setTimeout(shuffleColors, 1000);

https://plnkr.co/edit/p71QmO

我已经在Linux上的Chromium(49(和Firefox(45(中尝试过。 前者似乎爆炸得更快,但两者都有问题。 在内存分析器中,它都不会显示,但 about:memory 显示选项卡在增长。

我从文档中的理解是,向选择添加过渡会用相同的名称替换任何以前的过渡(包括空名称(,但我的假设是为实现过渡而创建的函数实际上并没有被抛弃。 但是我还没有设法让他们确认这一点或解决这个问题。

所以,一个由两部分组成的问题:

  1. 这是 d3 转换的正确使用,还是有更正确的方法来完成我想要的?
  2. 如果我正确使用转换,如何让它停止泄漏内存?

编辑:

  1. 根据 Blindman67 的评论,我将其更改为使用 setTimeout 并稍微小一点。我试图模拟的原版更小更慢,但需要几个小时才能最终变大,所以我试图加快速度。 这个版本似乎仍在增长,至少对我来说是Chromium上的。
  2. 我观察到d3_selectionPrototype.transition每次都会使用递增的ID制作一个新d3_transition,但如果旧的ID被垃圾收集,那很好。我仍然无法指出它是否或为什么被保留。

我相当确定它必须在这里做这篇文章:

function shuffleColors() {
    squares.interrupt().transition().duration(500).style("fill", color);
    timer = setTimeout(shuffleColors, 1000);
}
var timer = setTimeout(shuffleColors, 1000);

每次调用函数shuffleColors()时,它都会再次调用自身,实质上是一个没有基本情况的递归循环。它没有立即爆炸的原因是每次调用函数都会延迟 1000 毫秒,但是,我认为完成squares.interrupt().transition().duration(500).style("fill", color);所需的时间比调用setTimeout()的时间要长。因此,即使您每 1000 毫秒调用一次它,它也可能会以某种方式堆叠,因为某些颜色变化可能需要更多时间来处理。

虽然它在技术上不应该这样做,但知道JavaScript的异步性,它可能有一个角色。我建议这样做并报告结果:

function shuffleColors() {
    squares.interrupt().transition().duration(500).style("fill", color);
}
var timer = setInterval(shuffleColors, 1000);

如果需要,您也可以随时致电clearInterval(timer)。 创建setInterval()正是出于您实现setTimeout()的原因。

编辑:这可能无法完全工作,因为您可能仍然需要等待颜色更改完成,但是,这至少是一种更清洁的方法。您可以实现某种wait()函数来等待颜色更改完成。

虽然矢量 (SVG( 图像是轻量级的,但与解码 JPEG 图像相比,不断更改颜色或类似内容所需的处理量是巨大的。

如果使原始图像大小小得多,然后将其扩展到分辨率,则可能会找到更好的结果。您可以制作一个 100x100 的画布 SVG 并将其扩展到 2000x2000 或其他尺寸,这样它就不必绘制这么大的图像。