涉及 jQuery Ajax 请求的内存泄漏 [英] Memory leak involving jQuery Ajax requests

查看:22
本文介绍了涉及 jQuery Ajax 请求的内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个网页在 IE8 和 Firefox 中都出现内存泄漏;Windows 进程资源管理器中显示的内存使用情况会随着时间的推移不断增加.

以下页面请求unplanned.json" url,这是一个永远不会改变的静态文件(尽管我确实将我的 Cache-control HTTP 标头设置为 no-cache 以确保 Ajax 请求始终通过).当它得到结果时,它清除一个 HTML 表,循环它从服务器返回的 json 数组,并为数组中的每个条目动态地向 HTML 表添加一行.然后等待 2 秒并重复此过程.

这是整个网页:

<头><title>测试页</title><脚本类型=文本/javascript"src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script><身体><script type="text/javascript">函数启动(){$.getJSON("unplanned.json", resetTable);}功能重置表(行){$("#content tbody").empty();for(var i=0; i"+ "<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");}设置超时(开球,2000);}$(开球);<table id="content" border="1" style="width:100% ; text-align:center"><thead><tr><th>MPE</th><th>Bin</th><th>何时</th><th>过滤的</th><th>失败</th></tr></thead><tbody></tbody>

如果有帮助,这里是我发回的 json 的一个例子(这是一个包含数千个条目而不是一个条目的确切数组):

<预><代码>[{mpe_name: "DBOSS-995",request_time: "09/18/2009 11:51:06",垃圾箱:4,过滤增量:1,failed_delta: 1}]

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

  • 它只删除单个元素.所以你不能给它一个像`$("#content tbody tr")`这样的查询并期望它删除你指定的所有元素.
  • 您删除的任何元素都必须具有 `id` 属性.所以如果我想删除我的 `tbody`,那么我必须为我的 `tbody` 标签分配一个 `id`,否则它会给出一个错误.
  • 它会删除元素本身及其所有后代,因此如果您只是想清空该元素,那么您必须事后重新创建它(或将插件修改为清空而不是删除).

所以这是我上面修改的页面以使用 Toran 的插件.为简单起见,我没有应用任何一般性能建议 由彼得提供.这是现在不再出现内存泄漏的页面:

<头><title>测试页</title><script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script><身体><script type="text/javascript"><!--$.fn.removefromdom = 函数{如果 (!this) 返回;var el = document.getElementById(this.attr("id"));如果(!el)返回;var bin = document.getElementById("IELeakGarbageBin");//在删除el之前,递归删除它的所有子节点.while (el.childNodes.length > 0) {如果 (!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);如果 (!bin) {bin = document.createElement("DIV");bin.id = "IELeakGarbageBin";document.body.appendChild(bin);}bin.appendChild(el);bin.innerHTML = "";};var 重置 = 0;函数启动(){$.getJSON("unplanned.json", resetTable);}功能重置表(行){$("#content tbody").removefromdom();$("#content").append('<tbody id="id_field_required"></tbody>');for(var i=0; i<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>何时</th><th>过滤的</th><th>失败</th></tr></thead><tbody id="id_field_required"></tbody>

进一步我将保持我的问题不变,但值得注意的是,此内存泄漏与 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>");}设置超时(重置表,2000);}$(重置表);

解决方案

我不知道为什么 firefox 对此不满意,但我可以根据经验说在 IE6/7/8 中你必须设置 innerHTML = "";在要从 DOM 中删除的对象上.(如果你动态创建了这个 DOM 元素)

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

而是尝试像下面这样的东西(这是我写的一个 jQuery 插件来解决这个问题).

jQuery.fn.removefromdom = function(s) {如果 (!this) 返回;var bin = $("#IELeakGarbageBin");如果 (!bin.get(0)) {bin = $("<div id='IELeakGarbageBin'></div>");$("body").append(bin);}$(this).children().each(功能() {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 不一样

I have a webpage that's leaking memory in both IE8 and Firefox; the memory usage displayed in the Windows Process Explorer just keeps growing over time.

The following page requests the "unplanned.json" url, which is a static file that never changes (though I do set my Cache-control HTTP header to no-cache to make sure that the Ajax request always goes through). When it gets the results, it clears out an HTML table, loops over the json array it got back from the server, and dynamically adds a row to an HTML table for each entry in the array. Then it waits 2 seconds and repeats this process.

Here's the entire webpage:

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

If it helps, here's an example of the json I'm sending back (it's this exact array wuith thousands of entries instead of just one):

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

EDIT: I've accepted Toran's extremely helpful answer, but I feel I should post some additional code, since his removefromdom jQuery plugin has some limitations:

  • It only removes individual elements. So you can't give it a query like `$("#content tbody tr")` and expect it to remove all of the elements you've specified.
  • Any element that you remove with it must have an `id` attribute. So if I want to remove my `tbody`, then I must assign an `id` to my `tbody` tag or else it will give an error.
  • It removes the element itself and all of its descendants, so if you simply want to empty that element then you'll have to re-create it afterwards (or modify the plugin to empty instead of remove).

So here's my page above modified to use Toran's plugin. For the sake of simplicity I didn't apply any of the general performance advice offered by Peter. Here's the page which now no longer memory leaks:

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

FURTHER EDIT: I'll leave my question unchanged, though it's worth noting that this memory leak has nothing to do with Ajax. In fact, the following code would memory leak just the same and be just as easily solved with Toran's removefromdom jQuery plugin:

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

解决方案

I'm not sure why firefox isn't happy w/ this but I can say from experience that in IE6/7/8 you must set innerHTML = ""; on the object you want removed from the DOM. (if you created this DOM element dynamically that is)

$("#content tbody").empty(); might not be releasing these dynamically generated DOM elements.

Instead try something like the below (this is a jQuery plugin I wrote to solve the issue).

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 = "";
};

You would call this like so: $("#content").removefromdom();

The only issue here is that you need to re-create your table each time you want to build it.

Also, if this does solve your issue in IE you can read more about this in a blog post that I wrote earlier this year when I came across the same problem.

Edit I updated the plugin above to be 95% JavaScript free now so it's using more jQuery than the previous version. You will still notice that I have to use innerHTML because the jQuery function html(""); doesn't act the same for IE6/7/8

这篇关于涉及 jQuery Ajax 请求的内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆