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

Kla*_*aas 10 javascript memory-leaks d3.js

我有一个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);
Run Code Online (Sandbox Code Playgroud)

https://plnkr.co/edit/p71QmO

我在Linux上的Chromium(49)和Firefox(45)上尝试过它.它似乎在前者上爆炸得更快,但这对两者都是一个问题.它既没有出现在内存分析器中,也出现在:内存显示标签增长.

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

因此,这个由两部分组成的问题:

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

编辑:

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

Dar*_*man 1

I'm fairly certain it has to do this piece right here:

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

var timer = setTimeout(shuffleColors, 1000);
Run Code Online (Sandbox Code Playgroud)

Every time you call the function shuffleColors(), it calls itself again, essentially creating a recursion loop without a base case. The reason it doesn't blow up immediately is that every call of the function it delayed 1000ms, however, I think it takes longer for the squares.interrupt().transition().duration(500).style("fill", color); to finish than it is for setTimeout() to be called. So even though you call it every 1000ms, it could be stacking up in some fashion since some color changes may require more time to process.

Though it technically shouldn't do that, knowing how asynchronous JavaScript is, it may have a role. I would suggest doing this instead and reporting back the results:

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

var timer = setInterval(shuffleColors, 1000);
Run Code Online (Sandbox Code Playgroud)

You can also call clearInterval(timer) if you need to at any point. setInterval() was created for the very reason you implemented setTimeout().

Edit: This may not work completely as you may still have you wait for the color change to finish, however, it would at least be a cleaner approach. You may be able to implement some sort of wait() function to wait for the color change to complete.

Though vector (SVG) images are lightweight, the amount of processing it requires to constantly change colors or something like that is enormous compared to decoding a JPEG image.

You might find better results if you make the original image size much smaller and then expand it to your resolution. You could make a 100x100 canvas SVG and expand it to 2000x2000 or something, so it doesn't have to draw such a large image.