JQuery垃圾收集 – 这会干净吗?

许多文章(例如msdn )都说当涉及DOM对象和JS对象时,在某些浏览器中无法清除循环引用。

(IE 6根本无法完成,IE7只能在页面请求之间执行):

Javascript Native( Leaks ):

function leak(){ var elem = document.createElement("DIV"); document.body.appendChild(elem); elem.onclick = function () { elem.innerHTML = elem.innerHTML + "."; // ... }; } 

因为元素的onload属性通过闭包引用回自身,所以它创建了一个循环引用

elem [DOM] -> elem.onclick [JS] -> elem [DOM]

JQuery版本( 不泄漏 ):

 function leak(){ var elem = $('
'); $(document.body).append(elem); elem.click(function () { elem.html(elem.html() + "."); // ... }; }

在这种情况下,即使仍有循环引用,jQuery也会阻止所有相关浏览器中发生泄漏:

elem [JS] -> element [DOM] -> elem.onclick [JS] -> elem [JS]

我的问题:如果仍有循环引用,jQuery如何阻止泄漏?

jQuery代码中的最后一件事(在Sizzle的代码之前,它的选择器引擎)是这个(这是防止泄漏的代码):

 // Prevent memory leaks in IE // Window isn't included so as not to unbind existing unload events // More info: // - http://isaacschlueter.com/2006/10/msie-memory-leaks/ if ( window.attachEvent && !window.addEventListener ) { window.attachEvent("onunload", function() { for ( var id in jQuery.cache ) { if ( jQuery.cache[ id ].handle ) { // Try/Catch is to handle iframes being unloaded, see #4280 try { jQuery.event.remove( jQuery.cache[ id ].handle.elem ); } catch(e) {} } } }); } 

当你在jQuery中做任何事情时,它会存储它所做的事情(即函数)和什么(即DOM元素)。 onunload通过jQuery缓存从其自己的内部缓存的事件处理程序中移除函数(无论如何都存储事件而不是单个DOM节点)。

哦,行:

 if ( window.attachEvent && !window.addEventListener ) { 

确保它只在IE上运行。

JQuery只能确保在通过库进行所有操作时没有泄漏。 jQuery中有一些名为“empty”和“cleanData”的例程,你可以仔细查看它们究竟发生了什么,但基本上代码只是在释放它们之前从DOM元素中分离出它所知道的一切。 当您使用“.html()”或“.load()”覆盖元素内容时,会调用该例程。

我个人对这种情况下的“保证”等术语非常谨慎。

改写以进一步澄清

在提供的示例中,实际上有两个导致内存泄漏的原因。 第一个内存泄漏由于创建对闭包内的实时DOM节点的直接引用而显现。 诸如IE6的传统浏览器的垃圾收集器(JS和DOM)不能使这样的引用无效。 因此需要在函数末尾清空节点引用。

由于实时DOM元素作为属性/属性附加到jQuery对象,jQuery默认绕过这个,前面提到的垃圾收集器在确定空引用时没有任何问题。 如果jQuery对象具有空引用,则只需清除它,并将其属性/属性(在本例中为对实时DOM元素的引用)与其一起清除。

所以为了避免这种内存泄漏,就是让一个对象维护对live DOM节点的引用,然后引用你的闭包中的对象。 闭包只会维护对象的引用而不是live DOM元素,因为该引用属于对象。

 // will still leak, but not due to closure references, thats solved. function noLeak(){ var obj= { elem: document.createElement('div') } obj.elem.onclick = function(){ obj.elem.innerHTML = obj.elem.innerHTML + "."; } } 

这清除了最明显的循环引用,但仍然存在泄漏(onclick)。 该节点具有对函数的引用,该函数具有对对象的引用,该对象又具有对该节点的引用。 避免这种泄漏的唯一方法是从addEvent竞赛中学习(许多人努力解决这个漏洞;))。 巧合的是,所需的代码可以在其中找到,所以我的appologies没有提供代码;)

为事件系统创建包装器会添加更多代码,但这是必不可少的。 主要思想是添加一个公共eventHandler,它将事件委托给存储所需引用的事件缓存/系统。 在卸载事件中,缓存被清除,打破循环引用,允许垃圾收集器(JS和DOM)整理自己的角落。