あなたの問題はgetImageData
機能にありません。を保持する変数getImageData
が割り当てられているのは、リークを作成する方法です。
問題は、それdelete c
が失敗し (削除は変数名に影響を与えません)、ブラウザーが何も言わずに false を返すことです。
MDN 削除リファレンス
c = null
代わりに試してみてください。ループの各ステップで変数を再作成しないように、ループのc
外で変数を宣言するようにしてください。for
変更されたコードは次のとおりです。
function getImageData(){
var i = 0;
var c;
while(i++ < 100){
c = g.getImageData(0,0,1000, 1000);
// c = null; // <= check UPDATE to see why this doesn't work as expected
}
}
function toDataURL(){
var i = 0;
var c;
while(i++ < 100){
c = g.canvas.toDataURL();
// c = null; // <= check UPDATE to see why this doesn't work as expected
}
}
同じブラウザでコードをまったく試してみたところ、開発者ツールでメモリ プロファイルを使用すると、ガベージ コレクタによってメモリが完全にクリアされていることがわかりました。
開発者ツール ( ) でメモリ タイムラインを確認しますCtrl+Shift+i
。
メモリ プロファイルを有効にするには、フラグを付けて Chrome を起動する必要があります--enable-memory-info
。
アップデート:
コメントで既に述べたように、ガベージ コレクションは、到達できなくなったメモリ ブロック (オブジェクト) を再利用することによって機能します。
関数が戻ると、そのオブジェクトc
への参照を持つものが何も残っていないため、そのオブジェクトは自動的にガベージ コレクションに使用できるようになります。
働き方についても誤解がありますnull
。オブジェクト参照を に設定しても、オブジェクトnull
は「null」になりません。オブジェクト参照を null に設定します。
したがって、この場合、各getImageData
情報を格納するために割り当てられたメモリは、関数が戻るまでそこに残ります。はimage data
非常に大きなオブジェクトであり、キャンバスのサイズが大きいほど大きくなるため、巨大なループ (500 ループ以上、マシンによって異なります) では、関数が返される前にメモリでオーバーフローが発生し、ガベージ コレクターがトリガーされます。 .
次の記事をお勧めします:高速でメモリ効率の高い JavaScript の記述。よく説明されていて読みやすいです。
解決 !!!
これで、ガベージ コレクターが関数の戻り後にのみトリガーされることがわかりました。私の頭に浮かんだ 1 つの解決策は、関数を呼び出す関数をミリ秒単位で遅らせることです。getImageData
そうすれば、各 getImageData 呼び出しの後に関数が戻ることが保証されます。
以下のコードを試してみましたが、10000回の反復でも機能します! 完了するまでに多くの時間がかかりますが、メモリ リークなしで完了します!)
自分で試してみてください:
<!DOCTYPE html>
<html>
<head>
<title>CanvasRenderingContext2D#getImageData bug fixed</title>
<script type="text/javascript">
var g;
function init(){
g = document.getElementById('canvas').getContext('2d');
g.fillStyle = "blue";
g.fillRect(10, 10, 100, 100);
g.fillStyle = "green";
g.fillRect(60, 60, 100, 100);
}
function getImageData(){
var c = g.getImageData(0,0,1000, 1000);
}
var total = 0;
var iterations = 100;
function test(){
var i = 0;
while(i++ < iterations){
setTimeout(function(){
getImageData();
total++;
//console.log(total);
if(total == iterations){
alert("" + total+" getImageData functions were completed!!!")
}
}, 0.01); // defer
}
alert("" + (i-1) + " iterations completed. Wait for the return of all getImageData");
}
</script>
</head>
<body onload="init()">
<button onclick="test()">call getImageData several times</button><br>
<canvas id='canvas' width='600px' height='500px'/>
</body>
</html>