【问题标题】:Does this javascript pattern encourage memory leaks?这种 javascript 模式是否鼓励内存泄漏?
【发布时间】:2013-04-25 14:13:51
【问题描述】:

我发现自己对 javascript 垃圾收集以及如何最好地鼓励它感到很困惑。

我想知道的与特定模式有关。我对模式本身是否被认为是一个好主意不感兴趣,我只是对浏览器垃圾收集器如何响应感兴趣,即引用会被释放和收集还是会导致泄漏。

想象一下这种模式:

   TEST = {     
                init : function(){                        
                      this.cache = {
                           element : $('#element')
                      };
                },
                func1 : function(){
                       this.cache.element.show();                     
                },
                func2 : function(){                                            
                       TEST.cache.element.show(); 
                },
                func3 : function(){
                       var self = this;                      
                       self.cache.element.show(); 
                },
                func4 : function(){
                       var element = this.cache.element;
                       element.show();
                }
                func5 : function(){
                        this.auxfunc(this.cache.element);
                }
                auxfunc1 : function(el){
                         el.show();
                }
                func6 : function(){
                       var el = getElement();
                       el.show();
                }
                getElement : function(){
                       return this.cache.element;
                }
    }

现在假设在页面加载时调用了TEST.init()

然后在不同的时间调用各种函数。

我想知道的是,在初始化时缓存元素或对象或其他任何东西,并以上述方式在应用程序的整个生命周期中引用它们,是否会对浏览器的垃圾收集器产生积极或消极的影响。

有什么不同吗?哪种方法最能鼓励垃圾收集?它们会导致泄漏吗?有没有循环引用?如果有的话在哪里?

【问题讨论】:

  • 您可以根据需要引用一个对象,只要它仍在使用中。如果它们已从 DOM 中删除,您应该清除对 DOM 对象的引用。
  • @PaulGrime 谢谢保罗,我想我明白这一点。但我担心的是,如果每次我调用 this.cache.element,它是否会在原始缓存引用的顶部的函数范围内创建一个新引用,该引用不会被垃圾收集?

标签: javascript jquery


【解决方案1】:

此代码本身不应导致任何内存泄漏。尤其是在现代浏览器中。它只是一个对象,就像您在任何脚本中都有大量其他对象一样。这完全取决于您引用它的位置、方式和时间。

基本规则是,只要在代码中的任何位置(直接/通过变量或间接/通过访问范围的闭包)不再引用一个对象,GC 就会标记并滑动。
如果您使用上面的代码,然后将其他内容分配给TEST,那么如果没有其他变量引用原始对象,则它所引用的对象字面量可能会被 GC'ed。

当然,预测 JS 中的内存泄漏是一门不精确的科学。根据我的经验,它们并不像某些人让你相信的那么普遍。 Firebug、Chrome 的控制台(分析器)和 IE 调试器可助您一臂之力。

前段时间我对这件事做了更多的挖掘resulting in this question。也许一些链接和发现对你有帮助......

如果没有,这里有几个技巧可以避免明显的泄漏:

  • 不要使用全局变量(它们实际上不会永久泄漏内存,但只要您的脚本运行就这样做)。
  • 不要将事件处理程序附加到全局对象(window.onload ==> 在 IE 从未完全卸载,因此事件处理程序不是 GC 'ed)
  • 只需将您的脚本包装在一个巨大的 IIFE 中,并尽可能使用严​​格模式。这样,您就可以创建一个可以在卸载时完全 GC 的作用域。
  • 测试,测试,再测试。 不要相信您阅读的有关该主题的每一篇博文!如果去年有什么问题,那么今天就不必如此了。当你读到这篇文章时,这个答案可能不再是 100% 准确,或者因为就在今天早上一些奇迹补丁对于 JS GC'ing 是由 erm...Paris Hilton 或其他一些外星生命形式编写的。

哦,回答你的评论问题:“但我担心的是,如果每次我调用 this.cache.element,它是否会在原始缓存顶部的函数范围内创建一个新引用哪些引用不会被垃圾回收?”
答案是不。因为this 将引用TEST 对象,而init 函数为该对象分配了一个属性cahche,它本身就是另一个对象字面量,其中1 个属性引用了一个jQ 对象。该属性 (cache) 以及可通过它访问的所有内容都将保留在内存中,直到您选择 delete TEST.cachedelete TEST。如果您要创建var cahce = {...};,则该对象将在ini 函数返回时被GC'ed,因为变量不能超过其范围,除非您使用闭包并间接公开某些变量。

【讨论】:

  • 谢谢,听起来很简单,这正是我需要知道的,因此将为您提供答案。
  • @gordyr:不客气……我添加了一些关于内存泄漏的更多信息,因为我仍然发现关于该主题的信息有太多相互矛盾的地方。有人说循环引用是唯一的原因,其他人说今天不适用。一篇文章指出,在闭包中引用 DOM 节点时 IE9 仍然会泄漏,其他人则说这在 IE8 中已修复......因此,链接到我的那个较旧的问题。这是一个冗长的文本,但其中也有一些信息:D。无论如何,快乐的编码
  • 太棒了...这非常有帮助。我感激不尽:)
  • 你的最终编辑就像我脑海中的一个灯泡。先生,您是天赐之物。干杯!
  • @gordyr:坚持下去,坚持下去。一旦你开始掌握 JS 作用域/引用/闭包和 this 关键字的工作方式,你很快就会发现是什么让 JS 成为如此富有表现力的语言
猜你喜欢
  • 1970-01-01
  • 2015-05-04
  • 1970-01-01
  • 2016-09-20
  • 1970-01-01
  • 2020-12-01
  • 2010-10-20
  • 1970-01-01
  • 2011-02-20
相关资源
最近更新 更多