Память утечки с jQuery в IE 7/8/9 и FF 4

Я боролся с некоторыми утечками памяти, используя jQuery во всех основных браузерах, и мне нужна помощь. Я прочитал много статей об утечках памяти, ссылках, циклических ссылках, замыканиях и т. Д. Я думаю, что все делаю правильно. Но я все еще наблюдаю увеличение памяти в IE9 и FF4 и сиротство в sIEve, что для меня просто не имеет смысла.

Я создал тестовый пример, чтобы продемонстрировать проблему. Тестовый пример в основном имеет большую таблицу из 500 строк, и пользователи могут щелкнуть каждую строку, чтобы войти в режим встроенного редактирования, в котором элементы добавляются с помощью jQuery. Когда пользователи выходят из режима встроенного редактирования, элементы удаляются.

В тестовом примере есть кнопка для имитации 100 щелчков для 100 строк, чтобы быстро усилить проблему.

Когда я запускаю его в sIEve, объем памяти увеличивается на 1600 КБ. увеличивается на 506, и остается 99 сирот. Интересно, что если я закомментирую .remove () в строке 123, объем памяти увеличится на 1030 КБ, объем используемой памяти увеличится на 11, и останется 0.

Когда я запустил его в IE9, объем памяти увеличился на 5900 КБ. При обновлении увеличивается еще на 1500 КБ, а при следующем запуске увеличивается еще на 1 КБ. Продолжение этого шаблона приводит к увеличению использования памяти

Когда я запускаю его в FF4, я получаю совсем другое поведение, если использую «100 кликов медленно» и «100 кликов быстро». Пиковое увеличение имитации медленных кликов составляет 8300 КБ, и требуется минута, чтобы опуститься до 3300 КБ. Пиковое увеличение эмуляции быстрых кликов составляет 27 700 КБ, затем требуется минута, чтобы опуститься до 4700 КБ. Обратите внимание, что это точно такой же код, который выполняется, только с меньшими задержками между выполнением. Обновление и следующий запуск продолжают увеличивать память с той же скоростью.

Пример кода:

<!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>
6
задан Will Ness 13 May 2012 в 13:23
поделиться