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

时间:2013-10-10 14:21:07

标签: javascript html5 dom canvas memory-leaks

下面我创建了一个简单的测试用例,显示当img标记的src设置为不同的dataUrls时,它会泄漏内存。看起来在将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>

如果您加载此html页面,则RAM使用量会随着时间的推移而逐渐增加,并且永远不会被清除。此问题看起来非常相似:Rapidly updating image with Data URI causes caching, memory leak。有什么办法可以防止这种内存泄漏吗?

5 个答案:

答案 0 :(得分:8)

我最终解决了这个问题。只有在更改image.src时才会发生内存膨胀,所以我只是完全绕过了Image对象。我这样做是通过获取dataUrl,将其转换为二进制(https://gist.github.com/borismus/1032746)然后使用jpg.js(https://github.com/notmasteryet/jpgjs)解析它。使用jpg.js然后我可以将图像复制回我的画布,因此Image元素完全被subbassed,从而无需设置其src属性。

答案 1 :(得分:2)

Panchosoft's answer在Safari中为我解决了此问题。

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

// 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"});
}

然后,在processImage渲染循环中:

// Destroy old image
if(temporaryImage) objectURL.revokeObjectURL(temporaryImage);

// Create a new image from binary data
var imageDataBlob = convertDataURIToBlob(imageData);

// Create a new object URL
temporaryImage = objectURL.createObjectURL(imageDataBlob);

// Set the new image
image.src = temporaryImage;

答案 2 :(得分:1)

我也遇到过这个问题,我确实认为这是一个浏览器错误。我在FF和Chrome中也看到了这种情况。至少Chrome曾经有类似的错误被修复。我认为它没有消失或没有完全消失。当我将img.src重复设置为唯一图像时,我看到内存不断增加。我已经向Chromium提交了一个错误,如果你想减轻一些重量:) https://code.google.com/p/chromium/issues/detail?id=309543&thanks=309543&ts=1382344039 (错误触发示例不一定每次都会生成一个新的唯一图像,但至少它很有可能)

答案 3 :(得分:0)

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

对于浏览器

jpeg-js

类似于@PaulMilham提到的jpgjs,但具有附加功能和更好的API(imo)。

对于NodeJS / Electron

sharp

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

由于我的程序在Electron中,所以我最终使用sharp,因为jpeg-js提到它是一种性能更高的替代方案(由于其内核是用本机代码编写的)。

答案 4 :(得分:0)

在处理图像后将源设置为固定的最小dataURI似乎可以解决此问题:

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'}));
相关问题