内存泄漏涉及的jQuery Ajax请求(Memory leak involving jQuery

2019-07-18 20:21发布

我的影片中泄露都IE8和Firefox内存网页; 在Windows进程资源管理器中显示的内存使用率只是不断增长随着时间的推移。

下面的页面请求“unplanned.json”的网址,这是一个静态文件,它不会改变(虽然我把我的Cache-control HTTP标头no-cache ,以确保Ajax请求始终经过)。 当它得到的结果,它清除了一个HTML表,遍历JSON数组它得到了从服务器返回,并动态地添加行到一个HTML表格数组中的每个条目。 然后它等待2秒,再重复这一过程。

这里就是整个网页:

<html> <head>
    <title>Test Page</title>
    <script type="text/javascript"
     src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>
</head> <body>
<script type="text/javascript">
    function kickoff() {
        $.getJSON("unplanned.json", resetTable);
    }
    function resetTable(rows) {
        $("#content tbody").empty();
        for(var i=0; i<rows.length; i++) {
            $("<tr>"
                + "<td>" + rows[i].mpe_name + "</td>"
                + "<td>" + rows[i].bin + "</td>"
                + "<td>" + rows[i].request_time + "</td>"
                + "<td>" + rows[i].filtered_delta + "</td>"
                + "<td>" + rows[i].failed_delta + "</td>"
            + "</tr>").appendTo("#content tbody");
        }
        setTimeout(kickoff, 2000);
    }
    $(kickoff);
</script>
<table id="content" border="1" style="width:100% ; text-align:center">
<thead><tr>
    <th>MPE</th> <th>Bin</th> <th>When</th> <th>Filtered</th> <th>Failed</th>
</tr></thead>
<tbody></tbody>
</table>
</body> </html>

如果有帮助,这里是我送回来的JSON的例子(它的这个确切的阵列wuith成千上万个条目,而不是一个):

[
    {
        mpe_name: "DBOSS-995",
        request_time: "09/18/2009 11:51:06",
        bin: 4,
        filtered_delta: 1,
        failed_delta: 1
    }
]

编辑:我已经接受Toran的非常有用的答案,但我觉得我应该张贴一些额外的代码,因为他的removefromdom jQuery插件,有一些限制:

  • 它不仅能消除单个元素。 所以,你不能给它像`$查询(“#内容TBODY TR”)`,并期望它删除所有你所指定的元素。
  • 你用它删除任何元素都必须有一个'id`属性。 所以,如果我想删除我的`tbody`,那么我必须指定一个`id`我`tbody`标签,否则它会给出一个错误。
  • 它消除了元件本身和其所有后代,所以,如果你只是想清空该元素,那么你就必须在事后重新创建(或修改插件清空,而不是删除)。

因此,这里是我的网页上面修改为使用Toran的插件。 为了简单起见,我没有申请任何性能一般建议由Peter提供 。 这里的页面现在不再内存泄漏:

<html>
<head>
    <title>Test Page</title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>
</head>
<body>
<script type="text/javascript">
<!--
    $.fn.removefromdom = function(s) {
        if (!this) return;

        var el = document.getElementById(this.attr("id"));

        if (!el) return;

        var bin = document.getElementById("IELeakGarbageBin");

        //before deleting el, recursively delete all of its children.
        while (el.childNodes.length > 0) {
            if (!bin) {
                bin = document.createElement("DIV");
                bin.id = "IELeakGarbageBin";
                document.body.appendChild(bin);
            }

            bin.appendChild(el.childNodes[el.childNodes.length - 1]);
            bin.innerHTML = "";
        }

        el.parentNode.removeChild(el);

        if (!bin) {
            bin = document.createElement("DIV");
            bin.id = "IELeakGarbageBin";
            document.body.appendChild(bin);
        }

        bin.appendChild(el);
        bin.innerHTML = "";
    };

    var resets = 0;
    function kickoff() {
        $.getJSON("unplanned.json", resetTable);
    }
    function resetTable(rows) {
        $("#content tbody").removefromdom();
        $("#content").append('<tbody id="id_field_required"></tbody>');
        for(var i=0; i<rows.length; i++) {
            $("#content tbody").append("<tr><td>" + rows[i].mpe_name + "</td>"
                + "<td>" + rows[i].bin + "</td>"
                + "<td>" + rows[i].request_time + "</td>"
                + "<td>" + rows[i].filtered_delta + "</td>"
                + "<td>" + rows[i].failed_delta + "</td></tr>");
        }
        resets++;
        $("#message").html("Content set this many times: " + resets);
        setTimeout(kickoff, 2000);
    }
    $(kickoff);
// -->
</script>
<div id="message" style="color:red"></div>
<table id="content" border="1" style="width:100% ; text-align:center">
<thead><tr>
    <th>MPE</th>
    <th>Bin</th>
    <th>When</th>
    <th>Filtered</th>
    <th>Failed</th>
</tr></thead>
<tbody id="id_field_required"></tbody>
</table>
</body>
</html>

进一步编辑:我会离开我的问题没有改变,虽然这是值得注意的是,此内存泄漏无关使用Ajax。 事实上,下面的代码将内存泄漏一样的,并可以很容易地与Toran的解决removefromdom jQuery插件:

function resetTable() {
    $("#content tbody").empty();
    for(var i=0; i<1000; i++) {
        $("#content tbody").append("<tr><td>" + "DBOSS-095" + "</td>"
            + "<td>" + 4 + "</td>"
            + "<td>" + "09/18/2009 11:51:06" + "</td>"
            + "<td>" + 1 + "</td>"
            + "<td>" + 1 + "</td></tr>");
    }
    setTimeout(resetTable, 2000);
}
$(resetTable);

Answer 1:

我不知道为什么Firefox是不开心瓦特/这一点,但我可以从经验说,在IE6 / 7/8必须设置的innerHTML =“”; 在对象上要从DOM中删除。 (如果你创建了这个DOM元素动态地即到)

$("#content tbody").empty(); 可能不被释放这些动态生成的DOM元素。

相反,你可以试试下面的(这是一个jQuery插件,我写来解决问题)。

jQuery.fn.removefromdom = function(s) {
    if (!this) return;

    var bin = $("#IELeakGarbageBin");

    if (!bin.get(0)) {
        bin = $("<div id='IELeakGarbageBin'></div>");
        $("body").append(bin);
    }

    $(this).children().each(
            function() {
                bin.append(this);
                document.getElementById("IELeakGarbageBin").innerHTML = "";
            }
    );

    this.remove();

    bin.append(this);
    document.getElementById("IELeakGarbageBin").innerHTML = "";
};

你会调用这个像这样: $("#content").removefromdom();

这里唯一的问题是,你需要重新创建表要建立它每次。

此外,如果这不解决您的问题,在IE浏览器,你可以在博客中了解更多关于这个职位 ,我在今年早些时候写道,当我遇到同样的问题来了。

编辑我更新了插件上面是95%的JavaScript现在有空,所以它使用更多的jQuery比以前的版本。 你仍然会发现,我必须使用innerHTML,因为jQuery的功能HTML(“”); 不作用于相同的对IE6 / 7/8



Answer 2:

我不知道泄漏,但你的resetTable()函数是非常低效的。 先尝试修复这些问题,看看你在哪里结束。

  • 不要追加到一个循环的DOM。 如果你必须做的DOM操作,然后附加到一个文档片段,然后将该片段转移到DOM。
  • 但是innerHTML的比DOM操作速度,无论如何,所以使用,如果你能。
  • 商店的jQuery设置到本地变量 - 无需每次都重新运行选择。
  • 另外店内反复在一个局部变量的引用。
  • 当在任何类型的集合进行迭代,存储在一个局部变量的长度了。

新的代码:

<html> <head>
    <title>Test Page</title>
    <script type="text/javascript"
     src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>
</head> <body>
<script type="text/javascript">
$(function()
{
    var $tbody = $("#content tbody");

    function kickoff() {
        $.getJSON("test.php", resetTable);
    }

    function resetTable(rows)
    {
        var html = ''
          , i = 0
          , l = rows.length
          , row;
        for ( ; i < l; i++ )
        {
            row = rows[i];
            html += "<tr>"
                + "<td>" + row.mpe_name + "</td>"
                + "<td>" + row.bin + "</td>"
                + "<td>" + row.request_time + "</td>"
                + "<td>" + row.filtered_delta + "</td>"
                + "<td>" + row.failed_delta + "</td>"
            + "</tr>";
        }
        $tbody.html( html );
        setTimeout(kickoff, 2000);
    }

    kickoff();
});
</script>
<table id="content" border="1" style="width:100% ; text-align:center">
<thead>
    <th>MPE</th> <th>Bin</th> <th>When</th> <th>Filtered</th> <th>Failed</th>
</thead>
<tbody></tbody>
</table>
</body> </html>

参考文献:

  • jQuery的Peformance规则
  • 加快您的Javascript


Answer 3:

请纠正我,如果我错了这里,但没有的setTimeout(FN)防止主叫方的内存空间释放? 从而使复位(行)方法中分配的所有变量/内存将保持分配直到循环完成了吗?

如果是这样的情况下,推弦的建设和appendTo逻辑,不同的方法可能会更好一点,因为这些物体将每个呼叫和唯一的返回值的内存后获得释放出来(在这种情况下,无论是串标记或咱这新的方法做了appendTo())将保留在内存中。

在本质上:

初始调用开球

- >通话可重置()

- > - > setTimeout的呼叫再次开球

- > - > - >调用复位()再次

- > - > - > - >继续,直到无限

如果代码从来没有真正解决,这棵树将继续增长。

释放在此基础上的一些内存的另一种方法是像下面的代码:

function resetTable(rows) {
    appendRows(rows);
    setTimeout(kickoff, 2000);
}
function appendRows(rows)
{
    var rowMarkup = '';
    var length = rows.length
    var row;

    for (i = 0; i < length; i++)
    {
        row = rows[i];
        rowMarkup += "<tr>"
                + "<td>" + row.mpe_name + "</td>"
                + "<td>" + row.bin + "</td>"
                + "<td>" + row.request_time + "</td>"
                + "<td>" + row.filtered_delta + "</td>"
                + "<td>" + row.failed_delta + "</td>"
                + "</tr>";      
    }

    $("#content tbody").html(rowMarkup);
}

这将标记添加到您的tbody然后完成堆栈的一部分。 我敢肯定的“行”每次迭代仍将保留在内存中; 然而,标记字符串,这样应该最终释放。

再次...它已经有一段时间,因为我看的setTimeout在这个低水平,所以我可能是完全错误的在这里。 在anycase,这将不会删除泄漏,唯一可能降低增长速度。 这取决于在使用处理的setTimeout的JavaScript引擎的垃圾收集器如何循环就像你在这里。



文章来源: Memory leak involving jQuery Ajax requests