40 votes

Comment puis-je empêcher les fuites de mémoire lors de la suppression d'images dans le DOM?

Il existe un bug connu dans webkit selon lequel lorsque vous supprimez une image du DOM, la mémoire associée à celle-ci n'est pas libérée.

Ceci pose problème pour les applications monopage qui chargent souvent des images.

Diverses solutions suggérées sont les suivantes :

Les 3 premières méthodes ne fonctionnent pas pour moi. Le principal inconvénient de recycler les éléments image est qu'il faut écrire beaucoup de code pour les gérer. Je charge un nouveau HTML via AJAX qui peut contenir des images, je ne connais donc pas nécessairement le nombre d'images qui seront chargées.

Existe-t-il d'autres solutions de contournement pour résoudre ce problème ?

2 votes

Je ne pense pas qu'un pool d'images nécessiterait autant de code ; et il peut facilement supporter un nombre arbitraire d'images.

0 votes

Et quel est le problème de supprimer l'attribut src?

0 votes

Selon la bibliothèque que vous utilisez (et si vous en utilisez une), vous pourriez envelopper la fonction de suppression du nœud pour gérer ce cas particulier en supprimant d'abord src avant de supprimer l'image elle-même.

13voto

sivatumma Points 1360

J'ai utilisé 3 types d'approches différents...

Premier. J'ai ajouté un ensemble d'images et j'ai laissé la collecte des déchets au navigateur. entrer la description de l'image ici

Cela a certainement été ramassé par le navigateur, mais après un certain temps, en veillant à ce qu'il n'y ait plus besoin des images qui ont été créées.

Deuxième. J'ai utilisé des motifs data:URI pour la source des 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);
}

entrer la description de l'image ici

Cela ressemblait, bien que légèrement meilleur pour moi car je regardais devant mon navigateur et observais une utilisation moindre de la mémoire, et la collecte des déchets était presque la même que ci-dessus.

Troisième. J'ai fait la collecte des déchets dans le 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";  // ou l'autre, les deux signifient la même chose ici.
    img.src = s;
    document.getElementById("myList1").appendChild(img);
    setTimeout(function(){img = null;}, 1000);
}

entrer la description de l'image ici

En un court laps de temps de test, j'ai cru que la dernière approche fonctionnait mieux, car elle libérait presque immédiatement l'espace sans attendre de voir s'il y avait encore besoin des images référencées.

En fin de compte, il vaut mieux faire la collecte des déchets vous-même, lorsque vous sentez absolument qu'il faut libérer quelque chose.

1 votes

En utilisant data:URI, vous passez à côté de tous les mécanismes de mise en cache HTTP du navigateur... dans une application de production, cela sera très remarquable.

0 votes

Point valide. Je les mets normalement tous dans css ou js et mets en cache ces ressources.

0 votes

Oui, mais il semble que dans cette application, les images sont le contenu, donc cela ne fonctionnera pas.

1voto

user3342816 Points 483

Le paramètre null était la solution ici au moins. (Firefox 69)

Dans un projet, je charge beaucoup d'images par lots de 1 à 5 pour les comparer. Les images ont en moyenne 8-9 Mo. Sans mettre les éléments img à null après les avoir supprimés, l'accumulation de RAM était significative et a fini par épuiser la mémoire disponible.

Donc, je suis passé de ceci :

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

à ceci :

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

Maintenant, il n'y a plus d'accumulation de RAM du tout.

1voto

paulscode Points 364

Un pool d'images de base devrait vous permettre de recycler les éléments img pour les réutiliser. Comme vous ne savez pas à l'avance combien d'images il y aura au total, faites simplement en sorte que la taille du pool s'agrandisse au besoin.

Quelque chose comme ceci devrait fonctionner :

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

Placez un conteneur "image_pool" caché sur votre page quelque part, pour cacher les images recyclées entre les utilisations :

Ensuite, chaque fois que vous avez besoin d'un nouvel élément img, appelez :

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

Et lorsque vous en avez fini avec :

recycleImg( 'my_image_id' );

Alternative jQuery

    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 $( "'" ).attr( { 'id': id, 'src': src, 'alt': alt } );
    }
    function recycleImg( id ) {
        $( "#" + id ).detach().appendTo( $("#image_pool") );
    }

Lorsque vous avez besoin d'un nouvel élément img :

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

(le recyclage fonctionne de la même manière que ci-dessus)

1 votes

Je ne suis pas sûr, mais détacher en JQuery détruit en fait le nœud, et appendTo crée un nouveau nœud dans le DOM. Il n'y a donc en fait aucun recyclage.

0 votes

Intéressant, je devrais regarder le code source pour vérifier. Quoi qu'il en soit, j'ajouterai ici une version ne utilisant pas les fonctions jQuery, juste pour référence.

0 votes

A posté la question ici : stackoverflow.com/questions/20905866/…

0voto

Michael R. Hines Points 389

Essayez de définir l'objet arborescence DOM JavaScript sur "null".

Tout d'abord, en utilisant quelque chose comme l'interface d'outil de développement Chrome, localisez l'objet dans la hiérarchie de l'arborescence DOM, inspectez-le et trouvez l'objet qui contient le binaire de votre image.

Essayez de définir cet objet sur null, comme var obj = null;.

Cela devrait indiquer à JavaScript que l'enfant n'est plus référencé et forcer la mémoire de l'objet à être libérée.

4 votes

Que voulez-vous dire exactement par "Les objets JavaScript eux-mêmes ne sont pas collectés par les ordures"? Besoin de plus d'explication, ou est-ce - en général - erroné.

0 votes

Désolé, Oui, ils sont collectés par le ramasse-miettes, mais pas toujours immédiatement - avez-vous essayé de le définir sur 'null' comme je l'ai suggéré? Est-ce que cela a aidé? Le fait d'utiliser Chrome pour localiser l'objet dans l'arborescence DOM a-t-il également été utile?

-1voto

hque1011 Points 39

Et si on utilisait les images comme arrière-plan pour les divs? Je crois que Flickr fait quelque chose comme ça pour leur application html5 mobile.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X