JavaScript与HTML5 getImageData的内存泄漏

8
我一直在尝试为我创建的javascript游戏制作一些视觉效果,我注意到我正在使用的一段代码来调节我的精灵的颜色,会使我的浏览器内存使用量不断增加,似乎没有限制。当我每帧(通过setInterval)从我的画布上下文中调用getImageData以创建一个新的ImageData对象进行重新着色时,这种内存泄漏只会发生在我的updateimage()函数中。我本以为javascript的垃圾回收器会销毁旧的对象,但如果没有,我不知道如何手动销毁它。任何有关解决此问题或修复此问题的帮助都将不胜感激。我的问题与这个问题非常相似:What is leaking memory with this use of getImageData, javascript, HTML5 canvas 然而,我需要我的代码在由setInterval调用的函数中每帧运行,他把它移到setInterval函数之外的解决方案对我来说不可行,我也不能留言问他是否找到了其他解决方法。
注意:此示例使用getImageData,无法通过将其放入.html文件中来进行本地测试,需要一个Web服务器。此外,它显然使用HTML5元素,因此某些浏览器无法使用它。
编辑:*已解决* 谢谢,下面的解决方案修复了它。我没有意识到您可以像在drawImage()中使用图像一样使用canvas元素,因此我重构了代码,现在使用的内存明显更少。如果有人想看看,我将上传这个更改后的代码到上面链接的页面。
1个回答

12

调用getImageData()不会导致内存泄漏。你的问题源于这一行代码:

TempImg.src = ImgCanvas.toDataURL("image/png");

实际上,每次执行该代码时,浏览器都会“下载”另一张图像并将其存储在内存中。因此,你最终会得到一个非常快速增长的缓存。你可以通过在Chrome中打开网站并检查开发者工具的资源选项卡 (ctrl+shift+i) 来轻松验证这一点。

你可以通过创建一个TempImgCanvas并将图像数据存储在该画布上来解决这个问题,而不是在每次调用你的updateimage()循环后更新图像对象。

我需要离开一会儿,但我会在几小时后回来,如果你愿意,我可以提供一个示例。


编辑: 我重新组织了一下内容,解决了你的缓存问题。你只需要进行两个更改。第一个是将你的updateimage()函数替换为以下代码:

function updateimage() {    
    var TempImgData = ImgContext.getImageData(0, 0, ImgCanvas.width, ImgCanvas.height);
    var NewData = TempImgData.data;
    var OrigData = ImgData.data;

    //Change image color
    var len = 4*ImgData.width*ImgData.height-1;
    for(var i=0;i<=len;i+=4) {
        NewData[i+0] = OrigData[i+0] * color.r;
        NewData[i+1] = OrigData[i+1] * color.g;
        NewData[i+2] = OrigData[i+2] * color.b;
        NewData[i+3] = OrigData[i+3];
    }

    //Put changed image onto the canvas
    ImgContext.putImageData(TempImgData, 0, 0);
}
第二步是将draw()中的最后一行更新为以下内容:

drawImg(ImgCanvas,Positions [i] .x,Positions [i] .y,Positions [i] .x + Positions [i] .y);

使用此更新的代码,我们只需引用原始基础(白色)图像数据,并根据每次通过updateimage()函数进行计算新值。当您调用getImageData()时,您会收到画布上图像数据的副本,因此如果您编辑了画布或数据,则另一个保持不变。您已经开始抓取原始基础图像数据,因此使用它而不必每次更新时重新获取它是有意义的。

此外,您会注意到我略微修改了更改图像颜色的循环。通过获取要访问/修改的实际数据数组的句柄,您可以避免每次通过循环解析父对象中的数组位置。这个技术实际上会导致相当不错的性能提升。您的性能一开始就很好,但是变得更加高效肯定没有坏处,因为这很简单明了。


大多数浏览器(可能全部)缓存所有已下载的图像。这样做是为了如果图像被多次使用,它们不必重新下载。 - beatgammit
谢谢,我也会在几个小时后回来,但这看起来是一个非常有前途的解决方案。自从我让它工作以来,我就没有想过去尝试修改那一部分代码。 - zookatron
@zookatron 没问题!我刚刚发布的更改中,我没有看到我们之前遇到的内存消耗增加的情况。 - Xenethyl
我使用了你发布的一些更改,除此之外,我还意识到我可以将“var TempImgData = ImgContext.getImageData(0, 0, ImgCanvas.width, ImgCanvas.height)”提前到我的setupimage()函数中,而不是每次调用它。现在它运行得非常好,非常感谢 :)顺便说一下,我也使用Chrome和开发者工具,但当我转到资源选项卡时,我没有看到任何特别显示图像和其他内容使用多少内存的信息,你是如何找到这些信息的? - zookatron
@zookatron 嗯,是的,你发现那行代码确实可以移动。重要的是我们有一个格式正确的图像数据数组来存储信息。至于 Chrome 资源信息,我看到的不是使用了一定量的内存,而是无限流式缓存图像。如果你加载原始文件并检查“资源”选项卡,在左侧的文件树下,选择Frames->(test.html)->Images,你会看到一个不断更新的页面图像流。同时,欢迎来到 Stack Overflow(SO)! :) - Xenethyl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接