IE 7/8/9和FF 4中jQuery的内存泄漏

IE 7/8/9和FF 4中jQuery的内存泄漏,jquery,memory-leaks,internet-explorer-9,firefox4,Jquery,Memory Leaks,Internet Explorer 9,Firefox4,我在所有主流浏览器中使用jQuery都遇到了一些内存泄漏问题,我正在寻求帮助。我读过很多关于内存泄漏、引用、循环引用、闭包等的文章。我认为我做的一切都是对的。但我仍然看到IE9和FF4中的记忆增加,以及筛子中的孤儿,这对我来说毫无意义 我创建了一个测试用例来展示这个问题。测试用例基本上有一个500行的大表,用户可以单击每一行进入一个内联编辑模式,在该模式中使用jQuery追加元素。当用户退出内联编辑模式时,元素将被删除 测试用例有一个按钮来模拟100行的100次点击,以快速放大问题 当我在sIE

我在所有主流浏览器中使用jQuery都遇到了一些内存泄漏问题,我正在寻求帮助。我读过很多关于内存泄漏、引用、循环引用、闭包等的文章。我认为我做的一切都是对的。但我仍然看到IE9和FF4中的记忆增加,以及筛子中的孤儿,这对我来说毫无意义

我创建了一个测试用例来展示这个问题。测试用例基本上有一个500行的大表,用户可以单击每一行进入一个内联编辑模式,在该模式中使用jQuery追加元素。当用户退出内联编辑模式时,元素将被删除

测试用例有一个按钮来模拟100行的100次点击,以快速放大问题

当我在sIEve中运行它时,内存增加了1600KB,使用量增加了506,并且有99个孤儿。有趣的是,如果我在第123行注释掉.remove(),内存增加了1030KB,使用中增加了11,并且有0个孤儿

当我在IE9中运行它时,内存增加了5900KB。一次刷新会增加1500KB,另一次运行会增加1K。继续此模式将继续增加内存使用

当我在FF4中运行它时,如果我使用“100次点击慢”和“100次点击快”,我会得到非常不同的行为。模拟慢速点击的峰值增加了8300KB,需要一分钟才能稳定到3300KB。模拟快速点击的峰值增加了27700KB,然后需要一分钟才能稳定到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>

var-clickcounter=0;
var-clickdelay=0;
var clickstart=0;
var-clicklimit=0;
$(文档).ready(函数(){
//创建一个包含500行的表
var tmp=[''];
对于(变量i=0;i<500;i++){
tmp.推力(“”);
tmp.推力('',i+1'');
tmp.push(“样品产品名称”);
tmp.push('28%);
tmp.push('22.50');
tmp.push('12');
tmp.push(“主动”);
tmp.push('23');
tmp.push('81');
tmp.推力(“”);
}
tmp.推力(“”);
$('body').append(tmp.join('');
//实时绑定单击事件处理程序以进入内联编辑
$('tr.w').live('click',函数(事件){
var jrow=$(此);
if(!jrow.find('.inedBottle').length){
createInlineEdit(jrow);
}
});
//这只是为了模拟连续行上的100次单击
$('#slow100')。单击(函数(){
单击开始=单击计数器;
单击计数器++;
var jrow=$(“#产品id”+点击计数器);
createInlineEdit(jrow);
点击延时=1000;
点击限制=100;
设置超时(单击模拟,单击延迟);
});
//这只是为了模拟连续行上的100次快速点击
$('#fast100')。单击(函数(){
单击开始=单击计数器;
单击计数器++;
var jrow=$(“#产品id”+点击计数器);
createInlineEdit(jrow);
点击延时=20;
点击限制=100;
设置超时(单击模拟,单击延迟);
});
});
//模拟单击下一行并等待延迟时间以单击下一行
函数clickemulate(){
如果((clickcounter-clickstart)%clicklimit==0)返回;
nextInlineEdit($(“#产品id”+点击计数器));
单击计数器++;
设置超时(单击模拟,单击延迟);
}
//为行输入内联编辑模式
函数createInlineEdit(jrow,lastjrow){
removeInlineEdit(lastjrow);
jrow.removeClass('editable').addClass('editing');
//找到每个单元格
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-battlecell=jrow.find('.battle');
var descdiv=productcell.find('.desc');
var product_id=jrow.attr('id')。替换(/^product_id/,'');
//替换为输入
html(“”);
html(“”);
glasscell.html(“”);
html(“”);
var tmp=[];
//对于一个输入,插入几个div和span以及输入。
//注:div.ined和下面的跨距和输入是筛中剩余的孤立项
tmp.推力(“”);
tmp.推力(“插入跨距1”);
tmp.推力(“插入跨距2”);
tmp.推力(“”);
tmp.推力(“”);
tmp.推力(“”);
tmp.推力(“”);
tmp.推力(“”);
tmp.推力(“”);
html(tmp.join(“”));
jrow.find('.inedVintage').focus().select();
}
//退出内联编辑模式
函数removeInlineEdit(jrow){
if(jrow&&jrow.length){
}否则{
jrow=$('tr.w.editing');
}
jrow.removeClass('editing').addClass('editable');
//注:div.ined和下面的跨距和输入是筛中剩余的孤立项
//筛选步骤:加载页面,点击“使用中清除”,点击页面上的“快速点击100次”
//如果删除被注释掉,那么sIEve不会将任何div.ined报告为孤立项,并且报告11正在使用中(div.ined似乎都是垃圾收集的)
//如果删除未注释,则sIEve将99个div.ined报告为孤立项,并报告506个正在使用中(未收集任何div.ined垃圾)
jrow.find('.ined').remove();
find('.inedBin').each(function(){
$(this.replaceWith)(this.defaultValue);
});
jrow.find('.inedClass').each(函数(){
$(this.replaceWith)(this.defaultValue);
});
jrow.find('.inedBottle').each(function(){
$(this.replaceWith)(this.defaultValue);
});
jrow.find('.inedCost')。每个(函数(){
$(this.replaceWith)(this.defaultValue);
});
jrow.find('.inedDesc').each(function(){
//由于div.ined位于此处下方,因此也会将其删除。
$(this.closest('.desc').html(this.defaultValue);
});
}
函数nextInlineEdit(jrow){
var nextjrow=jrow.nextAll('tr.w').first();
如果(下一个jrow.length){
createInlineEdit(nextjrow,jrow);
}否则{
removeInlineEdit(jrow);
}
}
表{页边距顶部:30px;}
td{边框:1px灰色虚线;}
按钮#槽100{位置:固定;左侧:0px;宽度:115px;}
按钮#fast100{位置:固定;左侧:120px;宽度:115px;}
100次点击慢了
100次快速点击

可能有一个问题是
 $('tr.w').live('click', function(event){
   var jrow = $(this);
   if (!jrow.find('.inedBottle').length) {
    createInlineEdit(jrow);
   }
 });
 $('tr.w').bind('click', function(event){
   var jrow = $(this);
   if (!jrow.find('.inedBottle').length) {
    createInlineEdit(jrow);
   }
 });
$(body).click( function (event) {
    $target = $(event.target);
    if ( $target.hasClass('w') ) {
        var jrow = $target;
        if (!jrow.find('.inedBottle').length) {
        createInlineEdit(jrow);
    }
});