Below I've created a simple test case that shows that when an img tag's src is set to different dataUrls, it leaks memory. It looks like the image data is never unloaded after the src is changed to something else.
<!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>
If you load this html page, RAM usage builds over time and is never cleaned up. This issue looks very similar: Rapidly updating image with Data URI causes caching, memory leak . Is there anything I can do to prevent this memory leak?
I ended up doing a work around for the issue. The memory bloat only happens when the image.src is changed, so I just bypassed the Image object altogether. I did this by taking the dataUrl, converting it into binary (https://gist.github.com/borismus/1032746) then parsing it using jpg.js (https://github.com/notmasteryet/jpgjs). Using jpg.js I can then copy the image back to my canvas, so the Image element is completely bybassed thus negating the need to set its src attribute.
Panchosoft's answer solved this for me in Safari.
This workaround avoids the memory increase by bypassing the leaking Image
object.
// 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"});
}
then, in the processImage
rendering loop:
// 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;
I'm also experiencing this issue and I do believe it's a browser bug. I see this happening in FF and Chrome as well. At least Chrome once had a similar bug that was fixed. I think it's not gone or not completely gone. I see a constant increase in memory when I set img.src repeatedly to unique images. I have filed a bug with Chromium, if you want to put some weight in :)
https://code.google.com/p/chromium/issues/detail?id=309543&thanks=309543&ts=1382344039
(The bug triggering example does not necessarily generate a new unique image every time around, but at at least it does with a high probability)