Javascript 将img.src设置为dataUrl会泄漏内存

Javascript 将img.src设置为dataUrl会泄漏内存,javascript,html,dom,canvas,memory-leaks,Javascript,Html,Dom,Canvas,Memory Leaks,下面我创建了一个简单的测试用例,它显示了当img标记的src设置为不同的dataurl时,它会泄漏内存。在src被更改为其他内容之后,似乎从未卸载图像数据 <!DOCTYPE html> <html> <head> <title>Leak Test</title> <script type="text/javascript"> canvas = null; context = nu

下面我创建了一个简单的测试用例,它显示了当img标记的src设置为不同的dataurl时,它会泄漏内存。在src被更改为其他内容之后,似乎从未卸载图像数据

<!DOCTYPE html>
<html>
  <head>
    <title>Leak Test</title>
    <script type="text/javascript">
      canvas = null;
      context = null;
      image = null;
      onLoad = function(event)
      {
        canvas = document.getElementById('canvas');
        context = canvas.getContext('2d');
        image = document.getElementById('image');
        setTimeout(processImage, 1000);
      }

      processImage = function(event)
      {
        var imageData = null;
        for (var i = 0; i < 500; i ++)
        {
          context.fillStyle = "rgba(" + Math.floor(Math.random() * 256) + "," + Math.floor(Math.random() * 256) + "," + Math.floor(Math.random() * 256) + "," + Math.random() +")";
          context.fillRect(0, 0, canvas.width, canvas.height);
          imageData = canvas.toDataURL("image/jpeg", .5);
          image.src = imageData;
        }
        setTimeout(processImage, 1000); 
      }
    </script>
  </head>
  <body onload="onLoad(event)">
    <canvas id="canvas"></canvas>
    <img id="image"></img>
  </body>
</html>

泄漏试验
canvas=null;
上下文=空;
image=null;
onLoad=函数(事件)
{
canvas=document.getElementById('canvas');
context=canvas.getContext('2d');
image=document.getElementById('image');
setTimeout(processImage,1000);
}
processImage=函数(事件)
{
var-imageData=null;
对于(变量i=0;i<500;i++)
{
context.fillStyle=“rgba(“+Math.floor(Math.random()*256)+”,“+Math.floor(Math.random()*256)+”,“+Math.floor(Math.random()*256)+”,“+Math.random()+”);
context.fillRect(0,0,canvas.width,canvas.height);
imageData=canvas.toDataURL(“image/jpeg”,.5);
image.src=图像数据;
}
setTimeout(processImage,1000);
}

如果加载此html页面,RAM的使用会随着时间的推移而增加,并且永远不会被清除。此问题看起来非常类似:。我能做些什么来防止内存泄漏吗

我最后为这个问题做了一个变通。内存膨胀只有在image.src被更改时才会发生,所以我完全绕过了image对象。我通过获取dataUrl,将其转换为二进制文件(),然后使用jpg.js()解析它来实现这一点。使用jpg.js,我可以将图像复制回我的画布,因此图像元素完全是Bybased的,因此无需设置其src属性。

我也遇到了这个问题,我确信这是一个浏览器错误。我在FF和Chrome中也看到了这种情况。至少Chrome曾经有一个类似的bug被修复了。我认为它还没有消失或者还没有完全消失。当我将img.src反复设置为唯一图像时,我看到内存不断增加。我用铬做了一个bug,如果你想增加一些重量:) (错误触发示例并不一定每次都会生成一个新的唯一图像,但至少很有可能会生成新的唯一图像)

在Safari中为我解决了这个问题

此解决方法通过绕过泄漏的
图像
对象来避免内存增加

// Methods to address the memory leaks problems in Safari
var BASE64_MARKER = ';base64,';
var temporaryImage;
var objectURL = window.URL || window.webkitURL;

function convertDataURIToBlob(dataURI) {
    // Validate input data
    if(!dataURI) return;

    // Convert image (in base64) to binary data
    var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
    var base64 = dataURI.substring(base64Index);
    var raw = window.atob(base64);
    var rawLength = raw.length;
    var array = new Uint8Array(new ArrayBuffer(rawLength));

    for(i = 0; i < rawLength; i++) {
        array[i] = raw.charCodeAt(i);
    }

    // Create and return a new blob object using binary data
    return new Blob([array], {type: "image/jpeg"});
}

其他答案中未提及的一些解决方案:

用于浏览器 与@PaulMilham提到的类似,但具有附加功能和更好的API(imo)

用于NodeJS/Electron 用于NodeJS的通用图像处理库,具有读取和写入jpeg、png等图像(作为文件或仅在内存中)的功能


由于我的程序使用的是Electron,我最终使用了jpeg js,因为
jpeg js
提到它是一种性能更高的替代方案(因为它的核心是用本机代码编写的)。

在处理图像后将源代码设置为固定的最小数据URI似乎可以解决我的问题:

const dummyPng = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=';

img.onload = () => {
    // ... process the image
    URL.revokeObjectURL(img.src);
    img.onload = null;
    img.src = dummyPng;
};
img.src = URL.createObjectURL(new window.Blob([new Uint8Array(data)], {type: 'image/png'}));

这是什么浏览器?您可以检查是否存在问题。GC是由浏览器内部处理的,因此您唯一能做的就是重写逻辑。这在Chrome和Firefox上都会发生,所以我假设这是根据规范或其他规定进行的。他们是否修复了错误?您可以在上查看错误状态。看起来它被标记为已修复。顺便说一句,WebKit(例如Safari和iOS上的Safari)也有内存泄漏的报告:这是问题的主要原因。谢谢还要注意的是,我可以使用javascript解决这个bug,但这并不理想,因为它需要比让浏览器处理更多的CPU。很酷,谢谢。我注意到了你的解决方案。这很好,也很有创意:)这些解决方法今天可以实现,这很酷,但我们在显示图像这样简单的事情上失败了,这仍然是一件很遗憾的事。你有什么样的代码可以告诉我吗?我正试图实现您所描述的东西,显然我不能很好地使用jpg.js。@JayhawksFan93请看。如果您的图像是jpeg格式,则只需使用开关的jpeg部分使用
decodeImage
方法。我遇到了类似的情况。我在
画布上反复使用
drawImage
来显示视频。我注意到记忆马上开始上升。我注意到你的作品中有
canvas
标签。这是在canvas上工作还是在实际元素中工作?我应该注意,它会立即上升,几分钟后崩溃。内存在1gb到1.5gb之间。我从未尝试过使用canvas 2d在画布上绘制视频,但它似乎相当普遍,所以如果它泄漏,我会感到惊讶。你能粘贴复制问题的代码吗?
const dummyPng = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=';

img.onload = () => {
    // ... process the image
    URL.revokeObjectURL(img.src);
    img.onload = null;
    img.src = dummyPng;
};
img.src = URL.createObjectURL(new window.Blob([new Uint8Array(data)], {type: 'image/png'}));