jQuery内存泄漏模式和原因

时间:2011-02-18 20:09:12

标签: javascript jquery performance memory-leaks garbage-collection

jQuery中哪些标准问题或编码模式会导致内存泄漏?


我在StackOverflow上看到了一些与ajax()调用或jsonp或DOM删除相关的问题。大多数jQuery内存泄漏问题都集中在特定问题或浏览器上,在jQuery中列出标准内存泄漏模式会更好。

以下是关于SO的一些相关问题:

网络上的资源:

2 个答案:

答案 0 :(得分:28)

据我所知,javascript中的内存管理是通过引用计数完成的 - 虽然对象的引用仍然存在,但它不会被释放。这意味着在单个页面应用程序中创建内存泄漏是微不足道的,并且可以绊倒来自java后台的使用。这不是JQuery特有的。以下面的代码为例:

function MyObject = function(){
   var _this = this;
   this.count = 0;
   this.getAndIncrement = function(){
       _this.count++;
       return _this.count;
   }
}

for(var i = 0; i < 10000; i++){
    var obj = new MyObject();
    obj.getAndIncrement();
}

在您查看内存使用情况之前,它看起来很正常。由于“_this”指针(增加i的最大值以使其更加显着),因此在页面处于活动状态时,MyObject的实例永远不会被释放。 (在旧版本的IE中,它们从未被释放,直到程序退出。)由于javascript对象可能在帧之间共享(我不建议尝试这个,因为它是严重的气质。),有些情况下甚至在现代浏览器中javascript对象可以比它们的意图长得多。

在jquery的上下文中,通常存储引用以节省dom搜索的开销 - 例如:

function run(){
    var domObjects = $(".myClass");
    domObjects.click(function(){
        domObjects.addClass(".myOtherClass");
    });
}

由于在回调函数中对它的引用,此代码将永远保留domObject(及其所有内容)。

如果jquery的编写者在内部错过了这样的实例,那么库本身就会泄漏,但更常见的是它是客户端代码。

第二个例子可以通过在不再需要时明确清除指针来修复:

function run(){
    var domObjects = $(".myClass");
    domObjects.click(function(){
        if(domObjects){
            domObjects.addClass(".myOtherClass");
            domObjects = null;
        }
    });
}

或再次进行查找:

function run(){
    $(".myClass").click(function(){
        $(".myClass").addClass(".myOtherClass");
    });
}

一个好的经验法则是在定义回调函数时要小心,并尽可能避免过多嵌套。

编辑:正如Erik的评论中指出的那样,您也可以使用this指针来避免不必要的dom查找:

function run(){
    $(".myClass").click(function(){
        $(this).addClass(".myOtherClass");
    });
}

答案 1 :(得分:16)

我会在这里贡献一个反模式,这是&#34;中链参考&#34;泄漏。

jQuery的优势之一是它的链接API,它允许您继续更改,过滤和操作元素:

$(".message").addClass("unread").find(".author").addClass("noob");

在该链的末尾,您有一个jQuery对象,其中包含所有&#34; .message .author&#34;元素,但 对象引用并反对原始&#34; .message&#34;元素。您可以通过.end()方法与他们联系并为他们做点什么:

 $(".message")
   .find(".author")
     .addClass("prolific")
   .end()
   .addClass("unread");

现在以这种方式使用时,泄漏没有问题。但是,如果将链的结果分配给具有较长生命周期的变量,则对较早集的反向引用将保持不变,并且在变量超出范围之前无法进行垃圾回收。如果该变量是全局变量,则引用永远不会超出范围。

例如,让我们说你在2008年的一篇博客文章中读到$("a").find("b")更高效&#34;比$("a b")(即使它甚至不值得考虑这样的微优化)。您决定需要一个页面范围的全局来保存作者列表,以便您执行此操作:

authors = $(".message").find(".author");

现在你有一个带有作者列表的jQuery对象,但它也引用了一个jQuery对象,它是完整的消息列表。你可能永远不会使用它,甚至不知道它在那里,它会占用记忆。

请注意,泄漏只能通过从现有集合中选择 new 元素的方法进行,例如.find.filter.children等。 docs指示何时返回 new 集。如果链具有简单的非过滤方法(如.css),那么简单地使用链接API不会导致泄漏,所以这没关系:

authors = $(".message .author").addClass("prolific");