删除DOM时jQuery内存泄漏

Eli*_*ght 46 jquery memory-leaks

这是一个死简单的网页,它使用jQuery泄漏IE8中的内存(我通过观察我的iexplore.exe进程的内存使用量在Windows任务管理器中随着时间的推移而检测到内存泄漏):

<html>
<head>
    <title>Test Page</title>
    <script type="text/javascript" src="jquery.js"></script>
</head>
<body>
<script type="text/javascript">
    function resetContent() {
        $("#content div").remove();
        for(var i=0; i<10000; i++) {
            $("#content").append("<div>Hello World!</div>");
        }
        setTimeout(resetTable, 2000);
    }
    $(resetContent);
</script>
<div id="content"></div>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

显然即使在调用jQuery.remove()函数时我仍然会遇到一些内存泄漏.我可以编写自己的删除函数,不会发生内存泄漏,如下所示:

$.fn.removeWithoutLeaking = function() {
    this.each(function(i,e){
        if( e.parentNode )
            e.parentNode.removeChild(e);
    });
};
Run Code Online (Sandbox Code Playgroud)

这很好用,不泄漏任何内存.那为什么jQuery会泄漏内存呢?我创建了另一个基于的删除函数jQuery.remove(),这确实会导致泄漏:

$.fn.removeWithLeakage = function() {
    this.each(function(i,e) {
        $("*", e).add([e]).each(function(){
            $.event.remove(this);
            $.removeData(this);
        });
        if (e.parentNode)
            e.parentNode.removeChild(e);
    });
};
Run Code Online (Sandbox Code Playgroud)

有趣的是,内存泄漏似乎是由jQuery包含的每个调用引起的,以防止事件和与删除的DOM元素相关的数据的内存泄漏.当我调用该removeWithoutLeaking函数时,我的记忆会随着时间的推移而保持不变,但是当我打电话时removeWithLeakage它会继续增长.

我的问题是,每个电话怎么样

$("*", e).add([e]).each(function(){
    $.event.remove(this);
    $.removeData(this);
});
Run Code Online (Sandbox Code Playgroud)

可能是导致内存泄漏?

编辑:修复了代码中的拼写错误,经过重新测试,证明对结果没有影响.

进一步编辑:我已经向jQuery项目提交了一个错误报告,因为这似乎是一个jQuery错误:http://dev.jquery.com/ticket/5285

bob*_*nce 58

我认为大卫可能会涉嫌removeChild泄漏,但我无法在IE8中重现它...它可能会在早期的浏览器中发生,但这不是我们在这里所拥有的.如果我手动removeChild divs没有泄漏; 如果我改变jQuery使用outerHTML= ''(或移动到bin然后bin.innerHTML)而不是removeChild仍然存在泄漏.

在消除过程中,我开始remove在jQuery中攻击一些内容.1.3.2中的第1244行:

//jQuery.event.remove(this);
jQuery.removeData(this);
Run Code Online (Sandbox Code Playgroud)

注释掉那条线导致没有泄漏.

所以,让我们看看event.remove,它调用data('events')以查看是否有任何事件附加到元素.在data做什么?

// Compute a unique ID for the element
if ( !id )
    id = elem[ expando ] = ++uuid;
Run Code Online (Sandbox Code Playgroud)

哦.所以它为每个甚至尝试读取数据的元素添加了一个jQuery的uuid-to-data-lookup入口hack属性,其中包括你要删除的元素的每个后代!真傻.我可以通过在它之前放置这条线来短路:

// Don't create ID/lookup if we're only reading non-present data
if (!id && data===undefined)
    return undefined;
Run Code Online (Sandbox Code Playgroud)

这似乎在IE8中解决了这种情况的泄漏.不能保证它不会破坏jQuery的迷宫中的其他东西,但逻辑上它是有道理的.

据我所知,泄漏只是jQuery.cache对象(它是数据存储,而不是真正的缓存)随着为每个被移除的元素添加新密钥而变得越来越大.尽管removeData应该删除那些缓存条目,但是当您delete从Object获取密钥时,IE似乎不会恢复该空间.

(无论哪种方式,这都是我不理解的jQuery行为的一个例子.它在做什么应该是一个简单的简单操作的内幕做得太多了......其中一些是相当可疑的东西.整个使用expando的东西以及jQuery innerHTML通过正则表达式做什么以防止在IE中显示为属性只是破坏和丑陋.而使得getter和setter具有相同功能的习惯令人困惑,并且在这里导致bug.)

[奇怪的是,在内存实际耗尽之前,在jquery.js中偶尔留下泄密测试结果偶尔会给出完全虚假的错误......有些事情就像'意外命令',我注意到'nodeName是否为null第667行的一个对象,据我所知,甚至不应该运行,更不用说有一个检查nodeName为null!IE在这里没有给我很多信心......]

  • @bobince如果在jQuery的更高版本中修复了这个问题,你知道吗?我遇到了与上述问题相同的问题,但现在代码看起来不一样了,所以我不能按原样使用你的修复程序.我将不得不再次重新塑造迷宫...... (3认同)
  • 像往常一样,答案很好. (2认同)

Ben*_*Ben 5

似乎是在jQuery 1.5(23.2月版)中修复.我遇到了与1.4.2相同的问题并首先修复了dom,如上所述,然后尝试新版本.