2016-05-16 18 views
8

我有一個SVG映射和一個輪詢數據更改的時間間隔,並相應地更新地圖上的顏色。這一切都工作正常,除非我使用過渡淡入新的顏色。然後,標籤緩慢地消耗越來越多的內存,直到它崩潰。重複應用d3轉換造成的內存泄漏

我做了一個簡化的例子,顯示了相同的行爲:

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

我在鉻(49)和在Linux火狐(45)嘗試過。前者似乎更快速地爆炸,但這兩者都是問題。它在內存分析器中都沒有出現,但關於:內存顯示了標籤的增長。

我從文檔中得到的理解是,向選擇中添加一個轉換可以用相同的名稱替換之前的任何轉換(包括空名稱),但是我的假設是實現轉換所創建的函數實際上並未拋出出。但我沒有設法讓他們確認或解決這個問題。

於是,一個問題兩個部分:

  1. 那是一個正確使用D3轉變的,或者是有做什麼我要爲一個比較正確的做法?
  2. 如果我正確使用了轉換,我該如何才能停止內存泄漏?

編輯:

  1. 每從Blindman67的評論,我改變了它使用setTimeout和略小。我試圖模擬的原始尺寸越來越小,但需要幾個小時才能確定地變大,所以我試圖加快速度。這個版本似乎還在增長,至少對我而言,Chromium是如此。
  2. 我看到d3_selectionPrototype.transition每增加一個ID就會產生一個新的d3_transition,但如果舊的垃圾收集器沒有問題的話,那很好。而且我仍然無法指出是否保留它。
+5

我剛剛在您的shuffle Colors函數中添加了一個調試器關鍵字,並將其關聯到D3代碼中。它在呼叫中斷上做的第一件事是開始建立一個1600箇中斷列表,它將每個方塊作爲一個單獨的實體處理。我沒有深入,因爲問題是顯而易見的,你正在大塊地咀嚼記憶,當GC開始放慢速度時,會將setInterval放出,一旦發生這種情況,你只是在等待崩潰。使用setTimeout並在完成後開始下一個洗牌。不會泄漏你的內存溢出你的調用堆棧 – Blindman67

+0

我只是想說'interrupt()'相同。 –

+0

[使用d3轉換導致內存泄漏]可能的重複(https://stackoverflow.com/questions/26735417/using-d3-transitions-causes-memory-leak) – Fraser

回答

0

我相當肯定它必須在這裏做這一塊:

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或者其他東西,所以它不需要畫出如此大的圖像。