jQuery内存泄漏模式和原因

Thi*_*yya 44 javascript performance jquery garbage-collection memory-leaks

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


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

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

网上资源:

tof*_*arr 29

根据我的理解,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();
}
Run Code Online (Sandbox Code Playgroud)

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

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

function run(){
    var domObjects = $(".myClass");
    domObjects.click(function(){
        domObjects.addClass(".myOtherClass");
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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

第二个示例可以通过在不再需要时显式清除指针来修复:

function run(){
    var domObjects = $(".myClass");
    domObjects.click(function(){
        if(domObjects){
            domObjects.addClass(".myOtherClass");
            domObjects = null;
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

或者再次进行查找:

function run(){
    $(".myClass").click(function(){
        $(".myClass").addClass(".myOtherClass");
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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

function run(){
    $(".myClass").click(function(){
        $(this).addClass(".myOtherClass");
    });
}
Run Code Online (Sandbox Code Playgroud)

  • 原来的"MyObject"示例也是错误的.它不会造成内存泄漏.至少在Chrome等现代浏览器中没有.现代的javascript引擎不单独使用引用计数来进行垃圾收集.只要您中断对其中一个根垃圾收集器对象的引用,您的对象就会被删除.对于纯javascript对象,这只是意味着确保没有引导回到全局范围的对象的引用链.当你开始在DOM对象中混合时,它会变得有点复杂. (7认同)
  • jsfiddle.net/qTu6y/8不是内存泄漏.这是在同一对象上创建100000个单击处理程序的DIFFERENT实例的示例.当然,他们被保留,因为他们仍然是活跃的点击处理程序.如果您不相信我,请在点击处理程序中设置警报,运行代码,单击div,然后查看单次点击警报的次数. (3认同)
  • 这是一个例子:http://jsfiddle.net/qTu6y/8/您可以在分析器中使用Chrome的"拍摄快照",看看该代码块的每次运行都会占用大约20MB的RAM.(在测试它时,我确实点击了"脚本在chrome上使用了太多RAM错误") (2认同)
  • 关于"MyObject"示例 - 它肯定在IE6中泄露(即使在离开页面之后),它在IE7中泄露,并且它过去常常泄漏铬.我很高兴浏览器正在进入一个不再如此的地步 (2认同)

Dav*_*vin 16

我将在这里贡献一个反模式,这是"中链参考"泄漏.

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

$(".message").addClass("unread").find(".author").addClass("noob");
Run Code Online (Sandbox Code Playgroud)

在该链的末尾,您有一个带有所有".message .author"元素的jQuery对象,但对象返回并使用原始的".message"元素进行对象.您可以通过该.end()方法访问它们并对它们执行某些操作:

 $(".message")
   .find(".author")
     .addClass("prolific")
   .end()
   .addClass("unread");
Run Code Online (Sandbox Code Playgroud)

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

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

authors = $(".message").find(".author");
Run Code Online (Sandbox Code Playgroud)

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

需要注意的是泄漏只能与选择的方法中发生新的从现有的组的元素,例如.find,.filter,.children等.该文档指示当返回集.如果链具有简单的非过滤方法.css,那么简单地使用链接API不会导致泄漏,所以这是可以的:

authors = $(".message .author").addClass("prolific");
Run Code Online (Sandbox Code Playgroud)