jQuery内存泄漏与DOM删除

这里是一个简单的网页,使用jQuery在IE8中泄漏内存(通过观察我的iexplore.exe进程的内存使用情况随着时间在Windows任务pipe理器中的增长而检测到内存泄漏):

<html> <head> <title>Test Page</title> <script type="text/javascript" src="jquery.js"></script> </head> <body> <script type="text/javascript"> function resetContent() { $("#content div").remove(); for(var i=0; i<10000; i++) { $("#content").append("<div>Hello World!</div>"); } setTimeout(resetTable, 2000); } $(resetContent); </script> <div id="content"></div> </body> </html> 

显然,即使调用jQuery.remove()函数,我仍然遇到一些内存泄漏。 我可以编写我自己的删除function,没有内存泄漏,如下所示:

 $.fn.removeWithoutLeaking = function() { this.each(function(i,e){ if( e.parentNode ) e.parentNode.removeChild(e); }); }; 

这工作得很好,不会泄漏任何内存。 那么为什么jQuery泄漏内存? 我创build了另一个基于jQuery.remove()删除函数,这确实会导致泄漏:

 $.fn.removeWithLeakage = function() { this.each(function(i,e) { $("*", e).add([e]).each(function(){ $.event.remove(this); $.removeData(this); }); if (e.parentNode) e.parentNode.removeChild(e); }); }; 

有趣的是,内存泄漏似乎是由jQuery包含的每个调用引起的,以防止事件中的内存泄漏以及与被删除的DOM元素相关的数据。 当我调用removeWithoutLeaking函数,然后我的记忆保持不变,但是当我调用removeWithLeakage而不是它只是不断增长。

我的问题是,每个电话怎么样

 $("*", e).add([e]).each(function(){ $.event.remove(this); $.removeData(this); }); 

可能会导致内存泄漏?

编辑:修正代码中的错字,重新testing后,certificate对结果没有影响。

进一步的编辑:我已经提交了一个错误报告的jQuery项目,因为这似乎是一个jQuery的bug: http : //dev.jquery.com/ticket/5285

我认为大卫可能会涉及removeChild漏洞,但我不能在IE8中重现它…它可能发生在早期的浏览器,但是这不是我们在这里。 如果我手动removeChild divs没有泄漏; 如果我改变jQuery来使用outerHTML= '' (或移动到bin后bin.innerHTML),而不是removeChild仍然有泄漏。

在一个淘汰的过程中,我开始在jQuery中remove 。 1.3.2行1244:

 //jQuery.event.remove(this); jQuery.removeData(this); 

评论这条线路不会造成泄漏。

所以,让我们看看event.remove,它调用data('events') ,看看是否有任何事件附加到元素。 data在干什么?

 // Compute a unique ID for the element if ( !id ) id = elem[ expando ] = ++uuid; 

哦。 因此,它为每个甚至试图读取数据的元素添加jQuery的uuid-to-data-lookup条目hack属性,其中包括要删除的元素的每个后代。 多么愚蠢。 我可以通过把这条线放在它之前来短路:

 // Don't create ID/lookup if we're only reading non-present data if (!id && data===undefined) return undefined; 

这似乎解决了这种情况下在IE8泄漏。 不能保证它不会破坏是jQuery的迷宫中的其他东西,但逻辑上它是有道理的。

据我所知,泄漏只是jQuery.cache对象(这是数据存储,而不是一个真正的caching本身)变得越来越大,为每个删除的元素添加一个新的密钥。 尽piperemoveData应该删除这些caching条目,但是,当您从对象中delete一个键时,IE似乎不会恢复空间。

(无论哪种方式,这是一个我不明白的jQuery行为的例子,它是在一个简单的操作过程中做了太多的事情…其中一些是非常可疑的东西。使用expando和jQuery对innerHTML通过正则expression式做的事情, 以防止在IE中显示为一个属性,只是破碎和丑陋的。而使getter和setter相同的function的习惯是混乱,并在这里,导致了错误。 )

[奇怪的是,长时间的泄漏testing最终偶尔会在内存实际用完之前给jquery.js中的完全虚假的错误…有一些像'unexpected command',我注意到'nodeName为空或不一个对象'在667行,据我所知,甚至不应该运行,更不用说在那里有一个检查nodeName为空! IE在这里没有给我太多信心…]

似乎被修正在jQuery 1.5(23.二月版本)。 我遇到了与1.4.2相同的问题,并修复它与上面的dom删除,然后通过尝试新版本。

元素去除是一个固有的DOM问题。 哪一个会留在我们身边。 同上。

 jQuery.fn.flush = function() /// <summary> /// $().flush() re-makes the current element stack inside $() /// thus flushing-out the non-referenced elements /// left inside after numerous remove's, append's etc ... /// </summary> { return jQuery(this.context).find(this.selector); } 

我没有使用jQ来使用这个扩展。 尤其是在具有很多移除()和克隆()的页面中:

 $exact = $("whatever").append("complex html").remove().flush().clone(); 

而下一个确实有帮助:

 // remove all event bindings , // and the jQ data made for jQ event handling jQuery.unbindall = function () { jQuery('*').unbind(); } // $(document).unload(function() { jQuery.unbindall(); }); 

请参阅http://docs.jquery.com/JQuery_1.4_Roadmap上的jQuery 1.4路线图。 具体来说,“使用.outerHTML清除.remove()之后”部分处理由于调用remove函数而在IE中发生的内存泄漏问题。

也许你的问题将在下一个版本中解决。

如果您调用empty而不是remove它仍然泄漏?

 $("#content").empty(); 

JQuery 1.4.1有以下几点:

  cleanData: function (elems) { for (var i = 0, elem, id; (elem = elems[i]) != null; i++) { jQuery.event.remove(elem); jQuery.removeData(elem); } } 

这是我必须修改,以消除泄漏问题:

  cleanData: function (elems) { for (var i = 0, elem, id; (elem = elems[i]) != null; i++) { jQuery.event.remove(elem); jQuery.removeData(elem); jQuery.purge(elem); } } 

附加function:

  purge: function (d) { var a = d.childNodes; if (a) { var remove = false; while (!remove) { var l = a.length; for (i = 0; i < l; i += 1) { var child = a[i]; if (child.childNodes.length == 0) { jQuery.event.remove(child); d.removeChild(child); remove = true; break; } else { jQuery.purge(child); } } if (remove) { remove = false; } else { break; } } } },