使用 Data URI 快速更新图像导致缓存、内存泄漏 [英] Rapidly updating image with Data URI causes caching, memory leak

查看:44
本文介绍了使用 Data URI 快速更新图像导致缓存、内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个网页,可以从服务器快速流式传输 JSON 并显示它的一部分,大约每秒 10 次.其中一部分是 base64 编码的 PNG 图像.我找到了几种不同的显示图像的方法,但所有这些方法都会导致无限制的内存使用.它在几分钟内从 50mb 上升到 2gb.发生在 Chrome、Safari 和 Firefox 上.IE没试过.

I have a webpage that rapidly streams JSON from the server and displays bits of it, about 10 times/second. One part is a base64-encoded PNG image. I've found a few different ways to display the image, but all of them cause unbounded memory usage. It rises from 50mb to 2gb within minutes. Happens with Chrome, Safari, and Firefox. Haven't tried IE.

我首先通过查看 Activity Monitor.app 发现了内存使用情况——Google Chrome Renderer 进程不断地占用内存.然后,我查看了 Chrome 的资源检查器(View > Developer > Developer Tools, Resources),我看到它正在缓存图像.每次我更改 img src 或创建一个新的 Image() 并设置它的 src 时,Chrome 都会缓存它.我只能想象其他浏览器也在做同样的事情.

I discovered the memory usage first by looking at Activity Monitor.app -- the Google Chrome Renderer process continuously eats memory. Then, I looked at Chrome's Resource inspector (View > Developer > Developer Tools, Resources), and I saw that it was caching the images. Every time I changed the img src, or created a new Image() and set its src, Chrome cached it. I can only imagine the other browsers are doing the same.

有没有办法控制这个缓存?我可以关闭它,或者做一些偷偷摸摸的事情,让它永远不会发生吗?

Is there any way to control this caching? Can I turn it off, or do something sneaky so it never happens?

我希望能够在 Safari/Mobile Safari 中使用该技术.此外,如果有人有任何想法,我愿意接受其他快速刷新图像的方法.

I'd like to be able to use the technique in Safari/Mobile Safari. Also, I'm open to other methods of rapidly refreshing an image if anyone has any ideas.

以下是我尝试过的方法.每个都驻留在一个在 AJAX 完成时调用的函数中.

Here are the methods I've tried. Each one resides in a function that gets called on AJAX completion.

快.显示很好.泄漏得像疯了一样.

Fast. Displays nicely. Leaks like crazy.

$('#placeholder_img').attr('src', 'data:image/png;base64,' + imgString);

方法 2 - 将 img 替换为 canvas,并使用 drawImage

显示正常,但仍然泄漏.

Method 2 - Replace img with a canvas, and use drawImage

Displays fine, but still leaks.

var canvas = document.getElementById("placeholder_canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function() {
    ctx.drawImage(img, 0, 0); 
}   
img.src = "data:image/png;base64," + imgString;

方法 3 - 转换为二进制并替换 canvas 内容

我在这里做错了 - 图像显示很小,看起来像随机噪声.这种方法使用受控的内存量(增长到 100mb 并停止),但速度很慢,尤其是在 Safari 中(大约 50% 的 CPU 使用率,Chrome 中的 17%).这个想法来自这个类似的问题:DataSafari 中的 URI 泄漏(原为:HTML5 画布内存泄漏)

var img = atob(imgString);
var binimg = [];
for(var i = 0; i < img.length; i++) {
    binimg.push(img.charCodeAt(i));
}
var bytearray = new Uint8Array(binimg);

// Grab the existing image from canvas
var ctx = document.getElementById("placeholder_canvas").getContext("2d");
var width = ctx.canvas.width, 
    height = ctx.canvas.height;
var imgdata = ctx.getImageData(0, 0, width, height);

// Overwrite it with new data
for(var i = 8, len = imgdata.data.length; i < len; i++) {
    imgdata.data[i-8] = bytearray[i];
}

// Write it back
ctx.putImageData(imgdata, 0, 0);

推荐答案

我知道这个问题已经发布好几年了,但是这个问题在 Safari 浏览器的最新版本中仍然存在.所以我有一个适用于所有浏览器的明确解决方案,我认为这可以挽救工作或生命!

I know it's been years since this issue was posted, but the problem still exists in recent versions of Safari Browser. So I have a definitive solution that works in all browsers, and I think this could save jobs or lives!.

将以下代码复制到您的 html 页面中的某处:

Copy the following code somewhere in your html page:

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

然后当您收到 base64 格式的新帧/图像 base64Image(例如 data:image/jpeg;base64, LzlqLzRBQ...)并且您想要更新时一个 html <img/> 对象 imageElement,然后使用这个代码:

Then when you receive a new frame/image base64Image in base64 format (e.g. data:image/jpeg;base64, LzlqLzRBQ...) and you want to update a html <img /> object imageElement, then use this code:

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

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

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

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

根据需要尽可能多地重复最后一段代码,不会出现内存泄漏.此解决方案不需要使用画布元素,但您可以调整代码使其工作.

Repeat this last code as much as needed and no memory leaks will appear. This solution doesn't require the use of the canvas element, but you can adapt the code to make it work.

这篇关于使用 Data URI 快速更新图像导致缓存、内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆