使用jQuery的append()方法添加DOM元素似乎会导致内存泄漏?

6

我刚刚与一位客户会面,他们的Ajax webapp存在严重的内存泄漏问题。因此,我决定创建以下测试用例来演示该问题:

在下面的示例中,我使用了drip / Sieve进行内存分析(http://home.orange.nl/jsrosman/)

这个案例很简单:我有以下javascript代码:

<html>
    <head>    
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.4.min.js">
</script>    
</head>    
<script type="text/javascript">

    var lihtml = "<li class='green'>this is a test text</li>";

    function populatelist() {
        for (var i = 0; i < 10000; i++) {
            $('#listparent').append(lihtml);
        }    
    }    

    function clearlist() {
        $('#listparent').empty();
        if (typeof (CollectGarbage) == "function") {
            alert('gc');
            CollectGarbage();
        }    
    } 


    /* Alternative clearlist with Remove instead of Empty(), still leaks */
    function clearlist() {
        /* test remove the parent itself instead of empty below */
        $('#listparent').remove();
        $('body').append("<ul id='listparent'>");        
        //$('#listparent').empty();
        if (typeof (CollectGarbage) == "function") {
            alert('gc');
            CollectGarbage();
        }
    }

   /* Edit!, this is the most effective way to release memory so far */
   function clearlist() {
    $('#listparent').html("");
    if (typeof (CollectGarbage) == "function") {
        alert('gc');
        CollectGarbage();
    }
}
</html>   
</script>

<body>
    <button onclick="javascript:populatelist()">Populate list</button>
    <button onclick="javascript:clearlist()">Clear list</button>
    <ul id="listparent">
        <li>kjjk</li>
    </ul>    
</body>

</html>

每次点击populate list按钮时,会添加10000个li元素(以文本形式表示)。Clearlist调用jQuery empty()方法,应该清空DOM子树并使其符合GC条件。
我在sIEve中运行此案例,并且每次追加新元素时,内存使用量都会增加。即使RAM使用量达到了1.5GB,我仍然从未看到它进行垃圾回收或释放内存。即使我尝试为IE显式调用GC。
这是我在客户端看到的相同症状,他们使用Jquery Ajax获取列表数据而不是我的静态内容。
我是否以错误的方式创建了DOM?有人能告诉我为什么它没有被垃圾回收,我看不到任何其他对DOM元素的引用,也不知道为什么它们不应该被垃圾回收。另一个奇怪的行为是,有时在sIEve中单击empty list(调用jquery empty()方法时),内存使用量甚至会增加?
如果有人有任何建议,我会非常高兴。
更新, 我尝试使用$('#listparent').html("")代替,似乎可以正确释放DOM,在sIEve中已经被释放。我想这是目前最好的解决方案,尽管我无法解释为什么remove()和empty()似乎不起作用。也许它们只适用于静态添加的元素?

你好,只是好奇,你尝试在多个浏览器中运行过吗? - Prescott
No sIEve只在IE浏览器上工作,我还没有尝试FFX,主要是因为IE是该系统用户唯一使用的浏览器。 - user408346
实际上,$('#listparent').html(""); 比 empty() 和 remove() 都要好得多,似乎释放了整个 DOM。我无言以对。 - user408346
3个回答

1

是的,如果你像这样做,你可以获得巨大的改进。

var lihtml = "<li class='green'>this is a test text</li>",
    listring = "";

function populatelist() {
        for (var i = 0; i < 10000; i++) {
            listring += lihtml;
        }    

        $('#listparent').append(listring);

    }    
...

尽可能少地访问DOM。

不同之处在于您只进行了一次附加而不是一万次。您始终需要避免在循环内进行DOM操作。

编辑: 您是否尝试过删除ul然后突然重新创建它,而不是empty()您的ul?


这是一个很好的建议,我尝试了一下,但问题仍然存在,它仍然泄漏,并且empty()似乎无法释放DOM。开始对jquery失去信心了。 - user408346
1
我尝试了remove(),但仍然存在内存泄漏问题,但可能比增加内存而不是释放内存的empty()函数略微好一些。真正奇怪的是,$('#listparent').html("");可以解决问题并释放整个DOM和RAM内存。?!? - user408346
1
@user408346 你介意在你的问题中加入这些信息吗?同时,当你能够时,请将解决方案作为答案添加,并将其标记为正确。这对许多人可能非常有用。 - Daniel Dyson

1
我建议将HTML字符串附加到DOM中:
function populatelist() {
    for (var i = 0; i < 10000; i++) {
        //$('#listparent').append(lihtml);
        lihtml += "<li class='green'>this is a test text</li>";
    }    
}    
populateList();
$('#listparent').append(lihtml);

0
$('#listparent').html(""); 

这个方法在empty()和remove()都无法实现时起作用。我希望我知道为什么,但我猜这是一个解决方案。

/* 这是目前最有效的释放内存的方法 */

function clearlist() {
    $('#listparent').html("");
    if (typeof (CollectGarbage) == "function") {
        alert('gc');
        CollectGarbage();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接