代码之家  ›  专栏  ›  技术社区  ›  Eli Courtwright

删除DOM导致jQuery内存泄漏

  •  46
  • Eli Courtwright  · 技术社区  · 16 年前

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

    <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>
    

    jQuery.remove()

    $.fn.removeWithoutLeaking = function() {
        this.each(function(i,e){
            if( e.parentNode )
                e.parentNode.removeChild(e);
        });
    };
    

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

    有趣的是,内存泄漏似乎是由jQuery包含的每个调用引起的,这些调用是为了防止与被删除的DOM元素相关的事件和数据发生内存泄漏。当我打电话给 removeWithoutLeaking removeWithLeakage

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

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

    6 回复  |  直到 16 年前
        1
  •  57
  •   Community Mohan Dere    9 年前

    outerHTML= '' (或移动到bin,然后是bin.innerHTML),而不是删除Child,仍然存在泄漏。

    remove 在jQuery中。1.3.2中的第1244行:

    //jQuery.event.remove(this);
    jQuery.removeData(this);
    

    data('events') data

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

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

    jQuery.cache 对象(即数据存储,而不是真正的缓存)随着每个被删除的元素添加一个新键而变得越来越大。虽然removeData应该会删除这些缓存条目,但当您执行以下操作时,IE似乎无法恢复空间 delete

    (无论哪种方式,这都是我不欣赏的jQuery行为的一个例子。它在幕后做了太多的事情,而这应该是一个微不足道的简单操作……其中一些是非常有问题的东西。expando和 what jQuery does to innerHTML via regex to prevent that showing as an attribute in IE

        2
  •  5
  •   Ben    15 年前

    似乎在jQuery 1.5(23。二月版)。我在1.4.2版本中遇到了同样的问题,并首先通过如上所述的dom删除修复了它,然后尝试了新版本。

        3
  •  4
  •   Frumples    11 年前

    jQuery.fn.flush = function()
    /// <summary>
    /// $().flush() re-makes the current element stack inside $() 
    /// thus flushing-out the non-referenced elements
    /// left inside after numerous remove's, append's etc ...
    /// </summary>
    { return jQuery(this.context).find(this.selector); }
    

    我没有破解jQ,而是使用了这个扩展。尤其是在有很多removes()和clones()的页面中:

    $exact = $("whatever").append("complex html").remove().flush().clone();
    

    // remove all event bindings , 
    // and the jQ data made for jQ event handling
    jQuery.unbindall = function () { jQuery('*').unbind(); }
    //
    $(document).unload(function() { 
      jQuery.unbindall();
    });
    
        4
  •  2
  •   David Andres    16 年前

    http://docs.jquery.com/JQuery_1.4_Roadmap 具体来说,“在.remove()之后使用.outerHTML进行清理”一节处理了由于调用remove函数而在IE中发生的内存泄漏问题。

        5
  •  1
  •   Josh Stodola    16 年前

    如果你打电话,它还会漏水吗 empty 而不是 remove ?

    $("#content").empty();
    
        6
  •  1
  •   Basil    13 年前

    JQuery 1.4.1具有以下特性:

        cleanData: function (elems) {
            for (var i = 0, elem, id; (elem = elems[i]) != null; i++) {
                jQuery.event.remove(elem);
                jQuery.removeData(elem);
            }
        }
    

        cleanData: function (elems) {
            for (var i = 0, elem, id; (elem = elems[i]) != null; i++) {
                jQuery.event.remove(elem);
                jQuery.removeData(elem);
                jQuery.purge(elem);
            }
        }
    

        purge: function (d) {
            var a = d.childNodes;
            if (a) {
                var remove = false;
                while (!remove) {
                    var l = a.length;
                    for (i = 0; i < l; i += 1) {
                        var child = a[i];
                        if (child.childNodes.length == 0) {
                            jQuery.event.remove(child);
                            d.removeChild(child);
                            remove = true;
                            break;
                        }
                        else {
                            jQuery.purge(child);
                        }
                    }
                    if (remove) {
                        remove = false;
                    } else {
                        break;
                    }
                }
            }
        },