11

下面我创建了一个简单的测试用例,显示当 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 使用量会随着时间的推移而增加,并且永远不会被清理。这个问题看起来非常相似:使用 Data URI 快速更新图像会导致缓存、内存泄漏。我能做些什么来防止这种内存泄漏吗?

4

5 回答 5

9

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

于 2013-10-11T14:48:48.537 回答
4

Panchosoft 的回答在 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;
于 2018-07-17T08:18:45.997 回答
1

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

于 2013-10-21T12:06:15.783 回答
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'}));
于 2020-10-21T14:28:41.003 回答
0

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

对于浏览器

JPEG-JS

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

对于 NodeJS/电子

锋利的

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

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

于 2020-06-25T20:46:29.557 回答