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

标签 jquery memory-leaks internet-explorer-9 firefox4

我一直在所有主要浏览器中使用 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/

相关文章:

javascript - ajax 响应后值未更新

javascript - jQuery 单选按钮数学,仅在两个单选按钮均设置后才返回 true

Silverlight 4 ItemsControl 内存泄漏

java - 扩展 ByteArrayOutputStream 时 eclipse 中的资源泄漏警告

css - 我的布局需要修复什么才能与 Internet Explorer 一起使用?

css - ie 9 中背景图片不显示

javascript - 如何使用 jQuery 显示/隐藏事件重新加载 jQuery 幻灯片?

javascript - 如何在不使用ajax的情况下发送POST请求

android - 使用 LayoutInflater 膨胀 XML 时发生内存泄漏

svg - 如何在IE9中垂直居中SVG文本