Your problem is not with the getImageData
function. It's the way the variable that holds the getImageData
is assigned that create the leaks.
The problem is that delete c
will fail (delete doesn't affect variable names) and the browser silently returns false.
MDN delete reference
Try with c = null
instead. Try to declare the c
variable outside the for
loop, to avoid recreate the variable in each step of the loop.
Here is the modified code:
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
}
}
I tried the code exactly in the same browser and using the memory profile in developer tools I could see the memory being perfectly cleared by garbage collector.
Check the memory timeline in developer tools (Ctrl+Shift+i
).
To enable the memory profile you need to start Chrome with the flag --enable-memory-info
.
UPDATE:
As I’ve already said in comments, garbage collection works by reclaiming blocks of memory (objects) which are no longer reachable.
When a function returns, the object which c
points to is automatically available for garbage collection, because there is nothing left that has a reference to it.
There are also misconceptions about how null
works. Setting an object reference to null
doesn’t “null” the object. It sets the object reference to null.
So, in this case, the memory allocated to store each getImageData
information remains there until the function returns. Since the image data
is a very large object, and it's larger as larger are the canvas dimensions, in huge loops (let's say 500 loops or above, that depends on the machine) will cause overflow in memory before the function returns and the garbage collector be triggered.
I recommend the following article: Writing Fast, Memory-Efficient JavaScript. It's well explained and easy to read.
SOLUTION !!!
Now we know that the garbage collector is triggered only after a function return, one solution that came into my mind is defer the function that call the getImageData
by a fraction of millisecond. That way we guarantee that the function returns after each getImageData call.
I tried the code below and it work even for 10000 iterations! Spends a lot of time to finish, but it finishes and with no memory leaks!)
Try it by yourself:
<!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>
g.getImageData(0,0,10000, 9000);
twice will crash the thread on Windows 7 SP1, with Google Chrome27.0.1453.94
. – Soelch