2010-08-03 52 views
15

许多文章(例如msdn)都表示,某些浏览器无法清除循环引用,因为它涉及到DOM对象和JS对象。JQuery垃圾收集 - 这会干净吗?

(IE 6不能做它在所有和IE7只能页面请求之间做):

的Javascript原生(泄漏):

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 = $('<div></div>'); 
    $(document.body).append(elem); 
    elem.click(function() { 
     elem.html(elem.html() + "."); 
     // ... 
    }; 
} 

在这种情况下,jQuery的停止泄漏在甚至涉及所有浏览器发生,虽然仍有循环引用:

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

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

回答

6

在jQuery代码的最后一步(用于灒代码之前,它的选择器引擎)是这个(这是代码,以防止泄漏):

// 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浏览器。

+0

因此,它删除元素并打破IE6/6的循环引用。感谢您的洞察力 – 2010-08-11 12:21:57

2

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

就我个人而言,我很担心像这样的情况下的“保证”。

+0

我知道当你删除一个元素时,它会清除所有的东西(所以我的例子并不是一个完美的例子),但是例如在IE6中,如果在导航到下一页时存在循环引用,则内存将泄漏(当你不删除元素)。 – 2010-08-03 12:41:59

+0

啊,我明白你在说什么了。那么,另一种jQuery帮助的方式是避免直接将任何*钩到DOM节点上。 – Pointy 2010-08-03 12:53:33

+0

它并没有涉及到它,但从技术上讲,它将自己绑定到它,因此它将所有的jQuery事件和数据都绑定到它上面(当你调用.remove时可以正确清理它们)。 – 2010-08-03 13:00:25

1

改写为进一步澄清

实际上,有2个原因在所提供的示例内存泄漏。第一个内存泄漏是由于创建了一个对闭包内的活动DOM节点的引用而引起的直接。传统浏览器(如IE6)的垃圾收集器(JS & DOM)无法取消这些引用。因此需要在函数结束时清空节点引用。

由于活动DOM元素被附加到jQuery对象作为属性/属性,jQuery默认避开了这种情况,前面提到的垃圾收集器在确定空引用时没有问题。如果jQuery对象具有空引用,它将被简单地清除,并且它的属性/属性(在这种情况下是对活DOM元素的引用)。

所以为了避免这种内存泄漏,是让对象维护对活动DOM节点的引用,然后引用封闭对象中的对象。闭包将只保留对该对象的引用,而不会保留活动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比赛中学习(很多人努力解决这个漏洞;))。 Coincidentaly,需要的代码可以在其中找到,所以我的应用程序不提供代码;)

为事件系统创建包装会添加更多的代码,但是非常重要。主要想法是添加一个通用的eventHandler,将事件委托给存储所需引用的事件缓存/系统。在卸载事件中,缓存清除了循环引用,允许垃圾收集器(JS和DOM)整理自己的角落。

+0

所有的jQuery对象都有对它们操作的DOM元素的引用,所以技术元素 - > DOMELEMENT - > onload函数 - > elem。所以有一个循环参考发生。而在我真正的JS我需要使用闭包,所以我不能无效elem。 – 2010-08-03 12:50:21

+0

“A”循环引用不是内存泄漏的原因;)原因是活动DOM元素的“THE”循环引用。见上面的附录。 – BGerrissen 2010-08-10 22:17:54

+0

jQuery内部仍然有一个DOM元素的引用,即使事件/数据没有被赋值给DOM元素,仍然存在循环引用。 - 尽管最后一句(在答案中)使我感兴趣 – 2010-08-11 12:23:13