私はjavascriptを初めて使用し、毎秒Googleチャートをajaxデータで更新するコードでメモリリークを見つけるのに苦労しています。
私のコード(小さなテストケースに簡略化):
function TimeLine(id, max) {
this.chart = new google.visualization.LineChart(document.getElementById(id));
this.vals = new google.visualization.DataTable();
this.vals.addColumn('number', 'Index');
for (var i = 2; i < arguments.length; i++) {
this.vals.addColumn('number', arguments[i]);
}
this.numCols = arguments.length - 2;
this.max = max;
this.index = 0;
this.resourceOptions = {
'title': 'Memory allocation',
'width': 360,
'height': 300
};
}
TimeLine.prototype.Add = function () {
if (this.vals.getNumberOfRows() > this.max) {
this.vals.removeRow(0);
}
var row = [this.index];
for (var i = 0; i < arguments.length; i++) {
row.push(arguments[i]);
}
this.vals.addRow(row);
this.chart.draw(this.vals, this.options);
this.index++;
};
function onLoad() {
window.Timeline = new TimeLine('gauges', 15, 'Alloc');
drawCharts();
}
function drawCharts() {
window.Timeline.Add(window.Timeline.index%3);
setTimeout(drawCharts, 1000);
}
google.load('visualization', '1.0', {
'packages': ['corechart']
});
google.setOnLoadCallback(onLoad);
64 ビット Ubuntu でクロム バージョン 29.0.1547.62 を使用しています。
私は JS のスコープ ルールに慣れていないので、(できれば) スコープとガベージ コレクションについて簡単に推論できるように、グラフをオブジェクトでラップしました。SO に関する類似の質問を多数見てきましたが、私のコードでリークが発生しないようにすることができる限りではありません。メモリ タイムラインを使用すると、drawCharts が呼び出されるたびにメモリが上昇していることがわかります。そのメモリのほとんどは GC されているようですが、約 1 時間後にはそのタブで最大 300 MB になり、タブがクラッシュします。目標は、サーバーの 1 つの現在の負荷を監視するシステムとして、このタブを長期間維持できるようにすることですが、現在、停止するまで数時間しか維持できません。
プロファイル タブでヒープ スナップショットを使用してみました。drawCharts を数回呼び出す前後のスナップショットを比較すると、リークされたオブジェクトはチャート自体の SVG 要素であるように見えますが、これらの結果を誤って解釈している可能性があります。
問題を再現しました:
約 20 分後、Chrome の about:memory ページに約 150 MB の高いメモリ消費量が表示され始めます。この効果は、setTimeout を 100 ミリ秒に短縮することで、より速く確認できます。
編集:メモリ使用量の統計を修正