【问题标题】:How can I prevent memory leaks when removing images in the DOM?删除 DOM 中的图像时如何防止内存泄漏?
【发布时间】:2012-12-13 11:39:55
【问题描述】:

有一个known bug in webkit,当您从 DOM 中删除图像时,它不会释放与其关联的内存。

这是经常加载图像的单页应用程序的问题。

各种建议的解决方案是:

前 3 种方法对我不起作用。回收图像元素的主要缺点是它意味着编写大量代码来管理它。我正在通过 AJAX 加载可能包含图像的新 HTML,因此我不一定知道将加载的图像数量。

是否有其他解决方法来解决此问题?

【问题讨论】:

  • 我认为图像池不会有那么多代码;它可以轻松支持任意数量或图像。
  • 删除src属性有什么问题?
  • 刚刚更新了我的问题 - 删除 src 属性对我不起作用。
  • 如果您为每个图像手动调用 Image() 构造函数(链接错误的原因),您怎么可能不知道图像的数量。我不认为这个 bug 涵盖了使用 img 标签注入 html 的问题。

标签: javascript html webkit


【解决方案1】:

我使用了 3 种不同类型的方法...

首先。确实添加了一组图像并将垃圾收集留给浏览器。

它肯定是垃圾收集,但过一段时间,确保不需要已经创建的图像。

第二。为图像的 src 使用了 data:URI 模式。

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

这看起来很相似,但对我来说好一点,因为我在浏览器前观察并观察到使用的内存较少,垃圾收集几乎与上面相同。

第三。是否在代码中进行了垃圾回收。

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

在这么短的测试时间内,我认为最后一种方法效果更好,因为它几乎可以立即释放空间,而无需等待进一步查看是否需要参考图像。

总而言之,当您绝对觉得必须释放某些东西时,您最好自己使用垃圾收集。

【讨论】:

  • 通过使用data:URI,您将错过所有浏览器 HTTP 缓存...在生产应用程序中这将非常明显。
  • 有效点。我通常将它们全部放在cssjs 中并缓存这些资产。
  • 是的,但它出现在这个应用程序中,图像的内容,所以它不会工作。
  • 怎么不能?我不想反对你,但这种方法怎么行不通呢?放置一个包含所有src 变量的js 文件并缓存它。 img1 ="fjsa;lfs="img2="fjasdjfs="等。看起来即使是这个问题也可以正常工作。
  • @sivatumma 我认为 Ped 的意思是您事先不会知道所有图像。例如,考虑一个场景,您将图像上传到要显示的应用程序。你不可能事先知道要放入 JS 文件的文件名。
【解决方案2】:

一个基本的图像池应该允许您回收 img 元素以供重复使用。由于您无法提前知道总共有多少图像,因此只需根据需要扩展池大小即可。

这样的事情应该可以工作:

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

在页面某处放置一个隐藏的“image_pool”容器,以在使用之间隐藏回收的图像:

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

然后任何时候你需要一个新的 img 元素,调用:

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

当你完成它时:

recycleImg( 'my_image_id' );

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

当你需要一个新的 img 元素时:

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

(回收工作同上)

【讨论】:

  • 我不确定,但在 JQuery 中分离实际上会破坏节点,而 appendTo 在 DOM 中创建一个新节点。所以实际上根本没有回收利用。
  • 有趣,我得看看源代码来验证。不管怎样,我这里加一个不使用jQuery函数的版本,仅供参考。
  • 感谢您提出这个问题,@GuillermoGutiérrez 听起来该元素在 detach() 和 remove() 函数中都保留了,所以在我上面的代码的情况下,应该按预期正确回收该元素.
【解决方案3】:

尝试将 Javascript DOM 树对象设置为“null”。

首先,使用 chrome 开发者工具界面之类的工具,在 DOM 树层次结构中定位对象并检查它并找到包含图像二进制文件的对象。

尝试将该对象设置为空,例如var obj = null;

这应该告诉 Javascript 不再引用子对象并强制释放对象的内存。

【讨论】:

  • Javscript 对象本身不是垃圾回收”到底是什么意思?需要更多解释,或者通常是错误的。
  • 对不起,是的,它们是垃圾收集,但并不总是立即 - 您是否尝试按照我的建议设置为“null”?有帮助吗?使用 chrome 在 DOM 树中定位对象是否也有帮助?
【解决方案4】:

设置为null 至少是这里的解决方案。 (火狐 69)

在一个项目中,我以 1 到 5 个批次加载图像以进行比较。图片平均 8-9MB。如果在移除后不将 img 元素设置为 null,则 RAM 积累非常显着,并且会耗尽可用内存。

所以,从这里改变:

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

到这里:

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

现在根本没有 RAM 堆积。

【讨论】:

    【解决方案5】:

    如何将图像设置为 div 的背景?我相信 Flickr 正在为他们的移动 html5 应用程序做类似的事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-20
      • 1970-01-01
      • 2011-08-09
      • 2012-03-30
      • 1970-01-01
      • 1970-01-01
      • 2010-09-21
      相关资源
      最近更新 更多