Javascript leak error using HTML5 Canvas getImageData in Chrome browser for Mac OSX

this problem was fixed in the new version of chrome (version 35.0.1916.114)


In chrome for mac osx, the function CanvasRenderingContext2D # getImageData will lead to a memory leak, how can I avoid this problem, here is a test case and the result, this only happened in the Chrome browser, safari is ok

<!DOCTYPE html> <html> <head> <title>CanvasRenderingContext2D#getImageData bug in chrome</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 i = 0; while(i++ < 100){ var c = g.getImageData(0,0,1000, 1000); delete c; } } function toDataURL(){ var i = 0; while(i++ < 100){ var c = g.canvas.toDataURL(); delete c; } } </script> </head> <body onload="init()"> <button onclick="getImageData()">call getImageData 100 times - then memory will grow, can't GC</button> <button onclick="toDataURL()">call toDataURL 100 times - it is OK</button><br> <canvas id='canvas' width='600px' height='500px'/> </body> </html> 

enter image description here

+6
source share
1 answer

Your problem is not with the getImageData function. This is the way that the variable getImageData assigned a variable that creates a leak .

The problem is that delete c will fail (deletion does not affect variable names), and the browser silently returns false.

MDN removal link

Try c = null instead. Try declaring the variable c outside the for loop to avoid recreating the variable at 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 used the memory profile in the developer tools, I could see that the memory is perfectly cleared by the garbage collector.

Check the memory timeline in the developer tools ( Ctrl+Shift+i ).

To enable the memory profile, you need to run Chrome with the --enable-memory-info flag.

UPDATE:

As already mentioned in the comments, garbage collection works by restoring memory blocks (objects) that are no longer available.

When the function returns, the object that c points to is automatically available for garbage collection, because there is nothing left that has a link to it.

There are also misconceptions about how null works. Setting the object reference to null does not "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 image data is a very large object, and it is larger in size of the canvas, in huge cycles (say, 500 cycles or higher, which depends on the machine), it will overflow the memory before the function returns and garbage collector will be launched.

I recommend the following article: Writing fast, memory efficient JavaScript . It is well explained and easy to read.

DECISION!!!

Now we know that the garbage collector starts only after the function returns, and one of the solutions that occurred to me is to defer the function that calls getImageData for a fraction of a millisecond. Thus, we guarantee that the function will be returned after each call to getImageData.

I tried the code below and it works even for 10,000 iterations! Spends a lot of time to complete, but it ends without memory leaks!)

Try it 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> 
+4
source

Source: https://habr.com/ru/post/945864/


All Articles