我一直在所有主要浏览器中使用 jQuery 时遇到一些内存泄漏问题,并且正在寻求一些帮助。我读过很多关于内存泄漏、引用、循环引用、闭包等的文章。我认为我做的一切都是正确的。但我仍然看到 IE9 和 FF4 中的内存增加以及 sIEve 中的孤儿,这对我来说没有意义。
我创建了一个测试用例来展示该问题。测试用例基本上有一个 500 行的大表,用户可以单击每一行进入内联编辑模式,其中使用 jQuery 附加元素。当用户退出内联编辑模式时,元素将被删除。
测试用例有一个按钮可以模拟 100 行的 100 次点击,以快速放大问题。
当我在 sIEve 中运行它时,内存增加了 1600KB,使用中增加了 506,并且有 99 个孤儿。有趣的是,如果我注释掉第 123 行上的 .remove(),内存会增加 1030KB,使用中会增加 11,并且有 0 个孤儿。
当我在IE9中运行它时,内存增加了5900KB。刷新会增加 1500KB,另一次运行会增加 1K。继续这种模式会继续增加内存使用量
当我在 FF4 中运行它时,如果我使用“慢速 100 次点击”和“快速 100 次点击”,我会得到非常不同的行为。模拟慢点击的峰值增加了 8300KB,需要一分钟才能稳定到 3300KB。模拟快速点击峰值增加了 27,700KB,然后需要一分钟才稳定到 4700KB。请注意,这与执行的代码完全相同,只是执行之间的延迟较少。刷新和另一次运行会继续以相似的速度增加内存。
示例代码:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Strict//EN">
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script type="text/javascript">
var clickcounter = 0;
var clickdelay = 0;
var clickstart = 0;
var clicklimit = 0;
$(document).ready(function(){
// Create a table with 500 rows
var tmp = ['<table>'];
for (var i = 0; i < 500; i++) {
tmp.push('<tr id="product_id',i+1,'" class="w editable">');
tmp.push('<td class="bin">',i+1,'</td>');
tmp.push('<td class="productcell" colspan="2">Sample Product Name<div class="desc"></div></td>');
tmp.push('<td class="percentcost">28%</td>');
tmp.push('<td class="cost">22.50</td>');
tmp.push('<td class="quantity">12</td>');
tmp.push('<td class="status">Active</td>');
tmp.push('<td class="glass">23</td>');
tmp.push('<td class="bottle">81</td>');
tmp.push('</tr>');
}
tmp.push('</table>');
$('body').append(tmp.join(''));
// Live bind a click event handler to enter Inline Edit
$('tr.w').live('click', function(event){
var jrow = $(this);
if (!jrow.find('.inedBottle').length) {
createInlineEdit(jrow);
}
});
// This is just to emulate 100 clicks on consecutive rows
$('#slow100').click(function() {
clickstart = clickcounter;
clickcounter++;
var jrow = $('#product_id'+clickcounter);
createInlineEdit(jrow);
clickdelay = 1000;
clicklimit = 100;
window.setTimeout(clickemulate, clickdelay);
});
// This is just to emulate 100 rapid clicks on consecutive rows
$('#fast100').click(function() {
clickstart = clickcounter;
clickcounter++;
var jrow = $('#product_id'+clickcounter);
createInlineEdit(jrow);
clickdelay = 20;
clicklimit = 100;
window.setTimeout(clickemulate, clickdelay);
});
});
// Emulate clicking on the next row and waiting the delay period to click on the next
function clickemulate() {
if ((clickcounter - clickstart) % clicklimit == 0) return;
nextInlineEdit($('#product_id'+ clickcounter));
clickcounter++;
window.setTimeout(clickemulate, clickdelay);
}
// Enter inline edit mode for the row
function createInlineEdit(jrow, lastjrow) {
removeInlineEdit(lastjrow);
jrow.removeClass('editable').addClass('editing');
// Find each of the cells
var productcell = jrow.find('.productcell');
var bincell = jrow.find('.bin');
var percentcostcell = jrow.find('.percentcost');
var costcell = jrow.find('.cost');
var glasscell = jrow.find('.glass');
var bottlecell = jrow.find('.bottle');
var descdiv = productcell.find('.desc');
var product_id = jrow.attr('id').replace(/^product_id/,'');
// Replace with an input
bincell.html('<input class="inedBin" name="bin'+product_id+'" value="'+bincell.text()+'">');
costcell.html('<input class="inedCost" name="cost'+product_id+'" value="'+costcell.text()+'">');
glasscell.html('<input class="inedGlass" name="glass'+product_id+'" value="'+glasscell.text()+'">');
bottlecell.html('<input class="inedBottle" name="bottle'+product_id+'" value="'+bottlecell.text()+'">');
var tmp = [];
// For one input, insert a few divs and spans as well as the inputs.
// Note: the div.ined and the spans and input underneath are the ones remaining as orphans in sIEve
tmp.push('<div class="ined">');
tmp.push('<span>Inserted Span 1</span>');
tmp.push('<span>Inserted Span 2</span>');
tmp.push('<input class="inedVintage" name="vintage',product_id,'" value="">');
tmp.push('<input class="inedSize" name="size',product_id,'" value="">');
tmp.push('</div>');
tmp.push('<div class="descinner">');
tmp.push('<input class="inedDesc" name="desc'+product_id+'" value="'+descdiv.text()+'">');
tmp.push('</div>');
descdiv.html(tmp.join(''));
jrow.find('.inedVintage').focus().select();
}
// Exit the inline edit mode
function removeInlineEdit(jrow) {
if (jrow && jrow.length) {
} else {
jrow = $('tr.w.editing');
}
jrow.removeClass('editing').addClass('editable');
// Note: the div.ined and the spans and input underneath are the ones remaining as orphans in sIEve
// sIEve steps: load page, click "Clear in use", click "100 clicks fast" on the page
// If the remove is commented out, then sIEve does not report any div.ined as orphans and reports 11 in use (div.ined all appear to be garbage collected)
// If the remove is uncommented, then sIEve reports 99 of the div.ined as orphans and reports 506 in use (none of the div.ined garbage collected)
jrow.find('.ined').remove();
jrow.find('.inedBin').each(function() {
$(this).replaceWith(this.defaultValue);
});
jrow.find('.inedGlass').each(function() {
$(this).replaceWith(this.defaultValue);
});
jrow.find('.inedBottle').each(function() {
$(this).replaceWith(this.defaultValue);
});
jrow.find('.inedCost').each(function() {
$(this).replaceWith(this.defaultValue);
});
jrow.find('.inedDesc').each(function() {
// Since the div.ined is under here, this also removes it.
$(this).closest('.desc').html(this.defaultValue);
});
}
function nextInlineEdit(jrow) {
var nextjrow = jrow.nextAll('tr.w').first();
if (nextjrow.length) {
createInlineEdit(nextjrow, jrow);
} else {
removeInlineEdit(jrow);
}
}
</script>
<style>
table {margin-top: 30px;}
td {border: 1px dashed grey;}
button#slow100 {position: fixed; left: 0px; width: 115px;}
button#fast100 {position: fixed; left: 120px; width: 115px;}
</style>
</head>
<body>
<button id="slow100">100 clicks slow</button>
<button id="fast100">100 clicks fast</button>
</body>
</html>
最佳答案
我最近学到的技术是将事件绑定(bind)到您想要单击的每个对象,而是将一次单击绑定(bind)到页面,然后查看事件到已单击的对象。
由于多种原因,这要快得多:
- 您的浏览器需要执行的绑定(bind)规则要少得多(较旧的 FF 和 IE 处理能力较差,您可能在 Opera 或 Chrome 上没有注意到这些问题)
- 整个过程要快得多。
示例:
$(body).click( function (event) {
$target = $(event.target);
if ( $target.hasClass('w') ) {
var jrow = $target;
if (!jrow.find('.inedBottle').length) {
createInlineEdit(jrow);
}
});
这超出了我的想象。可能有比检查类(class)更好的方法,但我有点不舒服。
希望对您有帮助!
关于jquery - IE 7/8/9 和 FF 4 中 jQuery 的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6323273/