使用jQuery和内存泄漏问题

3
我已经使用jQuery超过几个月并花了几天时间研究Javascript内存泄漏问题。我有两个关于内存泄漏和jQuery的问题:
  1. 当我使用.bind(...)绑定事件时,如果我离开页面/刷新页面,我是否需要解绑它们(.unbind())以避免内存泄漏,或者jQuery会自动移除它们?

  2. 关于闭包,我读到如果使用不当,它们可能导致内存泄漏。如果我做如下操作:

    function doStuff( objects ){ //objects是一个包含DOM对象数组的jQuery对象 var textColor = "red"; objects.each(function(){ $(this).css("color", textColor ); }); }

    doStuff( $( "*" ) );

我知道上面的代码很愚蠢(有更好/更简单的方法),但我想知道它是否会导致.each出现循环引用/闭包问题,并且是否会导致内存泄漏。如果它确实会导致内存泄漏,我该如何重写它(通常使用类似的方法)以避免内存泄漏?

谢谢您的帮助。

编辑:我有另一个与第2个问题类似的情况(我想这就是第3个问题了)。

  1. If have something like this:

    function doStuff( objects ){ //iframe objects var textColor = "red";

    function innerFunction()
    {          
        $(this).contents().find('a').css("color", textColor );
    }
    
    objects.each(function(){   
        //I can tell if all 3 are running then we 
        //have 3 of the same events on each object, 
        //this is just so see which method works/preferred
    
        //Case 1
        $(this).load(innerFunction);
    
        //Case 2
        $(this).load(function(){
           $(this).contents().find('a').css("color", textColor );
        });
    
        //Case 3  
        $(this).load(function(){
           innerFunction();
        });
    });
    

    }
    doStuff( $( "iframe" ) );

上述有三种情况,我想知道哪种方法(或全部)会导致内存泄漏。此外,我想知道哪种是首选方法(通常我使用第二种)或更好的做法(或者如果这些不好,那么什么才是更好的方法?)。
谢谢!
4个回答

1

1) 不会。浏览器在页面加载之间清除所有内容。

2) 在当前形式下,不会出现内存泄漏,因为jquery的.each()函数不绑定任何东西,所以一旦它执行完毕,传递给它的匿名函数就不再可达,因此它关闭的环境(即整个闭包)也不再可达。因此垃圾回收引擎可以清理掉所有东西,包括对objects的引用。

但是,如果你使用了像$('div:eq(0)').bind()这样无害的东西代替.each()(我试图强调它不必是对大型objects变量的引用,只要它是一个单独的不相关元素),那么由于发送到.bind()的匿名函数关闭了objects变量,它将保持可达,因此不会被垃圾回收,从而导致内存泄漏。

避免这个问题的简单方法是,在执行函数的末尾设置objects = null;

我应该指出,我不熟悉JS垃圾回收引擎,因此可能存在合理的智能优化。例如,可以检查匿名函数是否尝试访问任何与其一起关闭的变量,如果没有,则可能将它们传递给垃圾收集器,从而解决问题。

如需进一步阅读,请查找有关JavaScript内存模型的参考资料,特别是环境模型和静态绑定。


1

有一些微妙的泄漏模式,你可能甚至没有意识到。看看我之前提出的一个类似问题的答案: jQuery 1.5 Memory leak in IE8

如果你在一个引用DOM节点的对象周围创建了一个闭包,就会形成一个泄漏的引用循环,不幸的是,它不能通过简单地解除绑定来纠正。你必须将对象中对DOM节点的引用设置为null。


我不同意,仅仅因为有一个对象引用了一些DOM节点,并不意味着存在泄漏。在这种特定情况下,该对象在函数执行结束时已不再可达,因此可以进行垃圾回收==>没有内存泄漏。 - davin
在这里的代码中不存在循环引用,所以虽然你在另一种情况下可能是对的,但这不适用于此处。 - davin
很有趣,乔丹能否提供一个例子?这会导致内存泄漏吗?var test = $('#id').click(function(){$(this).hide();}); - Mike Gleason jr Couturier
这不会泄漏,因为没有创建闭包。但是这样做 var test = {}; test.node = $('#id'); test.node.bind('click',function(){})); test.node.remove(); 你可以循环执行100次左右,观察内存使用量增加。 - jordancpaul
不,你非常清楚 - 内联代码被包裹了,我错过了var test语句。如果你调用test.remove(),它可能会泄漏。我说可能是因为我从未能够解决与jQuery团队的问题,即为什么会发生这种情况。尝试在IE中使用我链接的jsFiddle查看我在说什么。我的示例中闭包更微妙 - 匿名函数创建了一个环绕test的闭包,它引用了节点。 - jordancpaul
显示剩余2条评论

0
关于第一点,不,离开页面时绝对不需要使用.unbind()。至于第二点,我不是完全确定。

0

关于第一部分,有时候需要释放资源,尤其是当你有循环引用并且使用Internet Explorer(由于某些bug,在理论上你不应该这样做);)。Google地图在他们的v2中有一个函数来防止这种情况,我们需要在document.onunload(GUnload)上调用它。

关于第二部分,你没有循环引用。它会消耗大量内存,因为this必须具有自己的执行上下文才能访问textColor等。

当对象引用自身或闭包自调用时,即形成循环引用。也许还有其他情况。。

希望这可以帮助到您。


闭包可能会导致泄漏,而不一定存在循环引用。 - davin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接