简体   繁体   中英

How can I prevent memory leaks when removing images in the DOM?

There is a known bug in webkit that when you remove an image from the DOM, it doesn't free the memory associated with it.

This is an issue with single page apps that often load images.

Various suggested solutions are:

The first 3 methods don't work for me. The main drawback to recycling image elements is that it means writing lots of code to manage that. I'm loading new HTML via AJAX that may contain images, so I don't necessarily know the number of images that will be loaded.

Are there any other work arounds to fix this problem?

I have used 3 different types of approaches...

First . Did add a set of images and left the garbage collection to browser. 在此处输入图片说明

It definitely garbage collected, but after a while, making sure that there exists no need for the images which have been created.

Second . Used a data:URI patterns for the src for images.

var s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";

for (var i = 0; i < 100; i++){
    var img = document.createElement("img");
    img.src = s;
document.getElementById("myList1").appendChild(img);
setTimeout(function(){img = null;}, 1000);
}

在此处输入图片说明

This looked similar, though a bit better for me as I was watching in front of my browser and observed a lesser memory was used, and garbage collection was almost the same as above.

Third . Did garbage collection in code.

var s = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";

for (var i = 0; i < 100; i++){
    var img = document.createElement("img");
    img.src = "dot.png";  // or the other, both mean the same here.
    img.src = s;
document.getElementById("myList1").appendChild(img);
setTimeout(function(){img = null;}, 1000);
}

在此处输入图片说明

In this short time of testing, I believed that the last approach worked better, as it was almost freeing the space up immediately without waiting to see if there was any need for the referenced images further.

All in all, You better use garbage collection by yourself, when you absolutely feel like something must be freed off.

A basic image pool should allow you to recycle img elements for re-use. Since you don't know how many images there will be total ahead of time, just have the pool size expand as necessary.

Something like this should work:

    function getImg( id, src, alt ) {
        var pool = document.getElementById( 'image_pool' );
        var img = ( pool.children.length > 0 ) ? pool.removeChild( pool.children[0] ) : document.createElement( 'img' );
        img.id = id;
        img.src = src;
        img.alt = alt;
        return img;
    }
    function recycleImg( id ) {
        var img = document.getElementById( id );
        document.getElementById( 'image_pool' ).appendChild( img.parentNode.removeChild( img ) );
    }

Place a hidden "image_pool" container on your page somewhere, to hide the recycled images between usage:

<div id="image_pool" style="display:none;"></div>

Then any time you need a new img element, call:

document.getElementById( 'some_element_id' ).appendChild( getImg( 'my_image_id', 'images/hello.gif', 'alt_text' ) );

And when you are done with it:

recycleImg( 'my_image_id' );

jQuery alternative

    function getImg( id, src, alt ) {
        var img;
        if( $("#image_pool").children().length > 0 ) {
            return $("#image_pool img:first-child").attr( { 'id': id, 'src': src, 'alt': alt } ).detach();
        }
        return $( "<img />'" ).attr( { 'id': id, 'src': src, 'alt': alt } );
    }
    function recycleImg( id ) {
        $( "#" + id ).detach().appendTo( $("#image_pool") );
    }

When you need a new img element:

getImg( 'my_image_id', 'images/hello.gif', 'alt_text' ).appendTo( $( "#some_element_id" ) );

(recycling works the same as above)

Try setting the Javascript DOM tree object to "null".

First, using something like the chrome developer tool interface, locate the object in the DOM tree hierarchy and inspect it and find the object that contains the binary of your image.

Try setting that object to null, such as var obj = null; .

That should tell Javascript that the child is no longer referenced and force the object's memory to be freed.

Setting to null was the solution here at least. (Firefox 69)

In a project I am loading loads of images in batches of 1 - 5 for comparison. Images averaging 8-9MB. Without setting img elements to null after removal the RAM buildup was significant and as far as exhausting available memory.

So, changed from this:

function clear (n) {
    while (n.lastChild) {
        n.removeChild(n.lastChild);
    return n;
};

to this:

function clear (n) {
    let x;
    while (n.lastChild) {
        x = n.removeChild(n.lastChild);
        x = null;
    }
    return n;
};

Now there is no RAM buildup at all.

What about setting the images as backgrounds to divs? I believe Flickr is doing something like this for their mobile html5 app.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM