移除 DOM 后的 jQuery 内存泄漏
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1462649/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
jQuery memory leak with DOM removal
提问by Eli Courtwright
Here's a dead-simple webpage that leaks memory in IE8 using jQuery (I detect memory leaks by watching the memory usage of my iexplore.exe process grow over time in the Windows Task Manager):
这是一个非常简单的网页,它使用 jQuery 在 IE8 中泄漏内存(我通过在 Windows 任务管理器中观察 iexplore.exe 进程的内存使用量随时间增长来检测内存泄漏):
<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>
Apparently even when calling the jQuery.remove()
function I still experience some memory leakage. I can write my own remove function that experiences no memory leak as follows:
显然,即使在调用该jQuery.remove()
函数时,我仍然会遇到一些内存泄漏。我可以编写自己的没有内存泄漏的删除函数,如下所示:
$.fn.removeWithoutLeaking = function() {
this.each(function(i,e){
if( e.parentNode )
e.parentNode.removeChild(e);
});
};
This works just fine and doesn't leak any memory. So why does jQuery leak memory? I created another remove function based on jQuery.remove()
and this does indeed cause a leak:
这工作得很好并且不会泄漏任何内存。那么为什么 jQuery 会泄漏内存呢?我创建了另一个基于的删除函数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);
});
};
Interestingly, the memory leak seems to be caused by the each call which jQuery includes to prevent memory leaks from events and data associated with the DOM elements being deleted. When I call the removeWithoutLeaking
function then my memory stays constant over time, but when I call removeWithLeakage
instead then it just keeps growing.
有趣的是,内存泄漏似乎是由 jQuery 包含的 each 调用引起的,以防止与正在删除的 DOM 元素相关的事件和数据导致内存泄漏。当我调用该removeWithoutLeaking
函数时,我的记忆会随着时间的推移保持不变,但是当我调用removeWithLeakage
它时,它只会不断增长。
My question is, what about that each call
我的问题是,每个电话怎么办
$("*", e).add([e]).each(function(){
$.event.remove(this);
$.removeData(this);
});
could possibly be causing the memory leak?
可能会导致内存泄漏?
EDIT: Fixed typo in code which, upon retesting, proved to have no effect on the results.
编辑:修正了代码中的错字,经重新测试,证明对结果没有影响。
FURTHER EDIT: I have filed a bug report with the jQuery project, since this does seem to be a jQuery bug: http://dev.jquery.com/ticket/5285
进一步编辑:我已经向 jQuery 项目提交了一个错误报告,因为这似乎是一个 jQuery 错误:http: //dev.jquery.com/ticket/5285
回答by bobince
I thought David might be onto something with the alleged removeChild leak, but I can't reproduce it in IE8... it may well happen in earlier browsers, but that's not what we have here. If I manually removeChild the divs there is no leak; if I alter jQuery to use outerHTML= ''
(or move-to-bin followed by bin.innerHTML) instead of removeChild there is still a leak.
我认为 David 可能对所谓的 removeChild 泄漏有所了解,但我无法在 IE8 中重现它......它很可能发生在早期的浏览器中,但这不是我们在这里所拥有的。如果我手动 removeChild div 没有泄漏;如果我更改 jQuery 以使用outerHTML= ''
(或 move-to-bin 后跟 bin.innerHTML)而不是 removeChild,则仍然存在泄漏。
In a process of elimination I started hacking at bits of remove
in jQuery. line 1244 in 1.3.2:
在消除的过程中,我开始remove
在 jQuery 中进行一些黑客攻击。1.3.2 中的第 1244 行:
//jQuery.event.remove(this);
jQuery.removeData(this);
Commenting out that line resulted in no leak.
注释掉该行不会导致泄漏。
So, let's look at event.remove, it calls data('events')
to see if there are any events attached to the element. What is data
doing?
因此,让我们看看 event.remove,它会调用data('events')
以查看是否有任何附加到元素的事件。在data
做什么?
// Compute a unique ID for the element
if ( !id )
id = elem[ expando ] = ++uuid;
Oh. So it's adding one of jQuery's uuid-to-data-lookup entry hack properties for every element it even tries to readdata on, which includes every single descendent of an element you're removing! How silly. I can short-circuit that by putting this line just before it:
哦。因此,它为它甚至尝试读取数据的每个元素添加了 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;
which appears to fix the leak for this case in IE8. Can't guarantee it won't break something else in the maze that is jQuery, but logically it makes sense.
这似乎修复了 IE8 中这种情况下的泄漏。不能保证它不会破坏 jQuery 迷宫中的其他东西,但从逻辑上讲它是有道理的。
As far as I can work out, the leak is simply the jQuery.cache
Object (which is the data store, not a really a cache as such) getting bigger and bigger as a new key is added for every removed element. Although removeData should be removing those cache entries OK, IE does not appear to recover the space when you delete
a key from an Object.
据我所知,泄漏只是jQuery.cache
对象(它是数据存储,而不是真正的缓存)变得越来越大,因为为每个删除的元素添加了一个新键。尽管 removeData 应该可以删除这些缓存条目,但是当您delete
从对象中获取键时,IE 似乎无法恢复空间。
(Either way, this is an example of the sort of jQuery behaviour I don't appreciate. It is doing far too much under the hood for what should be a trivially simple operation... some of which is pretty questionable stuff. The whole thing with the expando and what jQuery does to innerHTML
via regex to prevent that showing as an attribute in IEis just broken and ugly. And the habit of making the getter and setter the same function is confusing and, here, results in the bug.)
(无论哪种方式,这是我不喜欢的那种 jQuery 行为的一个例子。它在引擎盖下做了太多的事情,这应该是一个非常简单的操作......其中一些是非常值得怀疑的东西。整个与 expando 的事情以及jQueryinnerHTML
通过正则表达式所做的以防止在 IE 中显示为属性的事情是破碎和丑陋的。使 getter 和 setter 具有相同功能的习惯令人困惑,在这里,会导致错误。)
[Weirdly, leaving the leaktest for extended periods of time ended up occasionally giving totally spurious errors in jquery.js before the memory actually ran out... there was something like ‘unexpected command', and I noted a ‘nodeName is null or not an object' at line 667, which as far as I can see shouldn't even have been run, let alone that there is a check there for nodeName being null! IE is not giving me much confidence here...]
[奇怪的是,在内存实际用完之前,长时间离开泄漏测试最终会在 jquery.js 中给出完全虚假的错误......第 667 行的一个对象',据我所知,它甚至不应该运行,更不用说检查 nodeName 是否为空了!IE在这里并没有给我太多信心......]
回答by Ben
Seems to be fixed in jQuery 1.5 (23. february version). I ran into the same problem with 1.4.2 and fixed it first with dom removal as above and then by trying the new version.
似乎已在 jQuery 1.5(二月 23 日版本)中修复。我在 1.4.2 中遇到了同样的问题,首先用上面的 dom 删除来修复它,然后尝试新版本。
回答by Ben
Element removal is an inherent DOM problem. Which is going to stay with us. Ditto.
元素移除是一个固有的 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); }
Instead of hacking jQ, I use this extension. Especially in the pages with lot of removes() and clones() :
我没有使用 jQ,而是使用这个扩展。特别是在有很多 removes() 和 clones() 的页面中:
$exact = $("whatever").append("complex html").remove().flush().clone();
And also the next one does help :
而且下一个确实有帮助:
// remove all event bindings ,
// and the jQ data made for jQ event handling
jQuery.unbindall = function () { jQuery('*').unbind(); }
//
$(document).unload(function() {
jQuery.unbindall();
});
回答by David Andres
See the jQuery 1.4 roadmap at http://docs.jquery.com/JQuery_1.4_Roadmap. Specifically, the section "Use .outerHTML to cleanup after .remove()" deals with memory leak issues occurring in IE due to the remove function being called.
请参阅http://docs.jquery.com/JQuery_1.4_Roadmap 上的 jQuery 1.4 路线图。具体来说,“在 .remove() 之后使用 .outerHTML 进行清理”部分处理了由于调用 remove 函数而在 IE 中发生的内存泄漏问题。
Perhaps your issues will be resolved with the next release.
也许您的问题将在下一个版本中得到解决。
回答by Basil
JQuery 1.4.1 has following:
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);
}
}
Here is what I had to modify to eliminate leaking problem:
这是我必须修改以消除泄漏问题的内容:
cleanData: function (elems) {
for (var i = 0, elem, id; (elem = elems[i]) != null; i++) {
jQuery.event.remove(elem);
jQuery.removeData(elem);
jQuery.purge(elem);
}
}
added 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;
}
}
}
},
回答by Josh Stodola
Does it still leak if you call empty
instead of remove
?
如果你调用empty
而不是它仍然泄漏remove
吗?
$("#content").empty();