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

时间:2016-05-16 03:48:44

标签: javascript d3.js memory-leaks

我有一个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)中尝试过它。它似乎在前者上爆炸得更快,但这两者都是一个问题。它既没有出现在内存分析器中,也出现在:内存显示标签增长。

我从文档中理解的是,向选择添加转换会替换任何先前的转换名称(包括空名称),但我的假设是为实现转换而创建的函数实际上并没有获得抛出。但我还没有设法让他们确认或解决这个问题。

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

  1. 这是正确使用d3过渡,还是有更正确的方法来做我想要的事情?
  2. 如果我正确使用转换,如何让它停止泄漏内存?
  3. 编辑:

    1. 根据Blindman67的评论,我将其更改为使用setTimeout并略小一些。我尝试模拟的原始版本越来越小,但是需要花费数小时才能显着增大,所以我试图加快速度。这个版本似乎仍在增长,至少对我来说是Chromium。
    2. 我观察到d3_selectionPrototype.transition每次都会使用递增ID创建一个新的d3_transition,但如果旧版本被垃圾收集,那就没问题了。而且我仍然不能指出它是否或为何被保留。

1 个答案:

答案 0 :(得分:1)

我相当肯定它必须在这里做这件事:

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

var timer = setTimeout(shuffleColors, 1000);

每次调用函数shuffleColors()时,它都会再次调用自身,实质上是创建一个没有基本情况的递归循环。它没有立即爆炸的原因是它延迟了1000ms的函数的每次调用,但是,我认为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或其他东西,所以它不必绘制如此大的图像。