【发布时间】:2010-11-30 04:02:18
【问题描述】:
我的网页在 IE8 和 Firefox 中都存在内存泄漏; Windows 进程资源管理器中显示的内存使用量随着时间的推移不断增长。
以下页面请求“unplanned.json” url,这是一个永远不会更改的静态文件(尽管我确实将我的 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 示例(这是一个包含数千个条目而不仅仅是一个条目的确切数组):
[
{
mpe_name: "DBOSS-995",
request_time: "09/18/2009 11:51:06",
bin: 4,
filtered_delta: 1,
failed_delta: 1
}
]
编辑:我已经接受了 Toran 非常有帮助的答案,但我觉得我应该发布一些额外的代码,因为他的 removefromdom jQuery 插件有一些限制:
- 它只删除单个元素。所以你不能给它一个像`$("#content tbody tr")`这样的查询并期望它删除你指定的所有元素。
- 使用它删除的任何元素都必须具有 `id` 属性。所以如果我想删除我的`tbody`,那么我必须为我的`tbody`标签分配一个`id`,否则会出错。
- 它会删除元素本身及其所有后代,因此如果您只是想清空该元素,则必须在之后重新创建它(或将插件修改为清空而不是删除)。
所以这是我上面的页面修改为使用 Toran 的插件。为了简单起见,我没有应用任何一般性能建议offered by 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);
【问题讨论】:
-
愚蠢的问题,但由于它在每次提取时都附加到正文中,它如何使用相同数量的内存?
-
它在附加到它之前清空 tbody。我希望清空的行会被垃圾收集,但如果我弄错了,肯定有人应该告诉我。
-
在 IE6/7/8 中你不能真正删除动态创建的 DOM 元素而不做一个奇怪的变通方法来使 innerHTML 为空(设置 innerHTML = "")
标签: javascript jquery ajax memory-leaks