用于mac OSX的Chrome浏览器中的HTML5 Canvas getImageData javascript内存泄漏

sam*_*sha 6 javascript memory-leaks google-chrome getimagedata html5-canvas

此问题已在新的chrome版本(版本35.0.1916.114)中得到修复


chrome for mac osx中,CanvasRenderingContext2D #getImageData函数会使内存泄漏,我该如何避免这个问题,这里是测试用例和结果,它只发生在chrome浏览器中,safari可以

<!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>
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

Gus*_*lho 4

你的问题不在于getImageData功能。这是保存变量的getImageData分配方式造成了泄漏.

\n\n

问题是delete c will fail (delete doesn\'t affect variable names) and the browser silently returns false.

\n\n

MDN 删除参考

\n\n

尝试用c = null代替。c尝试在外部声明变量for loop, to avoid recreate the variable in each step of the loop.

\n\n

这是修改后的代码:

\n\n
function getImageData(){\n    var i = 0;\n    var c;\n    while(i++ < 100){\n        c = g.getImageData(0,0,1000, 1000);\n        // c = null; // <= check UPDATE to see why this doesn\'t work as expected\n    }   \n}\n\nfunction toDataURL(){\n    var i = 0;\n    var c;\n    while(i++ < 100){\n        c = g.canvas.toDataURL();\n        // c = null; // <= check UPDATE to see why this doesn\'t work as expected\n    }   \n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我在同一个浏览器中尝试了完全相同的代码,并使用开发人员工具中的内存配置文件,我可以看到垃圾收集器完全清除了内存。

\n\n

在开发者工具中检查内存时间线(Ctrl+Shift+i).

\n\n

要启用内存配置文件,您需要使用标志启动 Chrome--enable-memory-info.

\n\n

更新:

\n\n

正如我\xe2\x80\x99ve在评论中已经说过的那样,垃圾收集通过回收不再可达的内存块(对象)来工作。

\n\n

当函数返回时,该对象c指向的对象自动可用于垃圾回收,因为没有任何东西可以引用它。

\n\n

对于如何运作也存在误解null。设置对象引用null doesn\xe2\x80\x99t \xe2\x80\x9cnull\xe2\x80\x9d the object. It sets the object reference to null.

\n\n

因此,在这种情况下,分配用于存储每个信息的内存getImageData将保留在那里,直到函数返回。自从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.

\n\n

我推荐以下文章:Writing Fast, Memory-Efficient JavaScript. It\'s well explained and easy to read.

\n\n

解决方案 !!!

\n\n

现在我们知道垃圾收集器仅在函数返回后才会触发,我想到的一个解决方案是将调用该函数的函数延迟getImageData几分之一毫秒。这样我们就可以保证函数在每次 getImageData 调用后返回。

\n\n

我尝试了下面的代码,即使迭代 10000 次它也能工作!花了很多时间来完成,但它完成了并且没有内存泄漏!)

\n\n

自己尝试一下:

\n\n
<!DOCTYPE html>\n<html>\n<head>\n<title>CanvasRenderingContext2D#getImageData bug fixed</title>\n<script type="text/javascript">\n\nvar g;\nfunction init(){\n    g = document.getElementById(\'canvas\').getContext(\'2d\');\n    g.fillStyle = "blue";\n    g.fillRect(10, 10, 100, 100);\n    g.fillStyle = "green";\n    g.fillRect(60, 60, 100, 100);\n}\n\nfunction getImageData(){        \n    var c = g.getImageData(0,0,1000, 1000);     \n}\n\nvar total = 0;\nvar iterations = 100;\n\nfunction test(){\n    var i = 0;\n\n    while(i++ < iterations){\n        setTimeout(function(){          \n            getImageData();\n            total++;\n            //console.log(total);\n            if(total == iterations){\n                alert("" + total+" getImageData functions were completed!!!")\n            }\n        }, 0.01); // defer\n    }\n    alert("" + (i-1) + " iterations completed. Wait for the return of all getImageData");\n}\n</script>\n</head>\n<body onload="init()">\n<button onclick="test()">call getImageData several times</button><br>\n<canvas id=\'canvas\' width=\'600px\' height=\'500px\'/>\n</body>\n</html>\n
Run Code Online (Sandbox Code Playgroud)\n

  • 我在 Chrome 27.0.1453.93、27.0.1453.94 和 29.0.1518.3 金丝雀中进行了测试。我的代码没有内存泄漏。内存在“while”循环期间增长,并在函数结束后被垃圾收集器清除。看来你面临的真正问题不是内存泄漏,而是内存溢出!我认为垃圾收集器在函数到达结束之前不会被触发。因此,使用巨大的画布或巨大的循环迭代将会在浏览器有机会触发垃圾收集器之前溢出内存。 (2认同)
  • 这个问题在新的chrome版本(版本35.0.1916.114)中得到修复 (2认同)