Javascript jQuery内存泄漏模式和原因

Javascript jQuery内存泄漏模式和原因,javascript,jquery,performance,memory-leaks,garbage-collection,Javascript,Jquery,Performance,Memory Leaks,Garbage Collection,jQuery中导致内存泄漏的一些标准问题或编码模式是什么 我已经看到了许多与StackOverflow上的ajax()调用、jsonp或DOM删除相关的问题。大多数jQuery内存泄漏问题都集中在特定的问题或浏览器上,最好在jQuery中列出标准内存泄漏模式 以下是一些关于SO的相关问题: 网上资源: 据我所知,javascript中的内存管理是通过引用计数来完成的——虽然对对象的引用仍然存在,但它不会被释放。这意味着在一个单页应用程序中创建内存泄漏是微不足道的,并且可能会绊倒

jQuery中导致内存泄漏的一些标准问题或编码模式是什么


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

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

网上资源:


据我所知,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();
}
function run(){
    var domObjects = $(".myClass");
    domObjects.click(function(){
        domObjects.addClass(".myOtherClass");
    });
}
由于回调函数中对domObject的引用,这段代码将永远保留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");
    });
}

我将在此提供一个反模式,即“中间链参考”泄漏

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

$(".message").addClass("unread").find(".author").addClass("noob");
在该链的末尾有一个jQuery对象,其中包含所有“.message.author”元素,但该对象引用了原始“.message”元素。您可以通过
.end()
方法访问它们,并对它们执行一些操作:

 $(".message")
   .find(".author")
     .addClass("prolific")
   .end()
   .addClass("unread");
现在使用这种方式时,不会出现泄漏问题。但是,如果将链的结果分配给具有较长生命周期的变量,则对早期集合的反向引用将保留,并且在该变量超出范围之前无法进行垃圾收集。如果该变量是全局变量,则引用永远不会超出范围

例如,假设您在2008年的某篇博客文章中看到,
$(“a”).find(“b”)
$(“ab”)
更“高效”(尽管它甚至不值得考虑这样的微观优化)。您决定需要一个页面范围的全局文件来保存作者列表,因此可以执行以下操作:

authors = $(".message").find(".author");
现在您确实有了一个包含作者列表的jQuery对象,但它也引用了一个jQuery对象,该对象是完整的消息列表。你可能永远不会使用它,甚至不知道它就在那里,它会占用你的记忆

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

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

这里有一个例子:您可以在分析器中使用Chrome的“Take a heapshot”来查看该代码块的每次运行都会消耗大约20MB的RAM。(在测试它时,我确实遇到了“脚本在chrome上使用了太多RAM错误”)$(这是)。在最后一种情况下,addClass的性能会更好,因为“this”表示一个核心JS DOM元素集合(这是JQuery对象通常使用适配器样式模式包装的内容)在jsfiddle.net/qTu6y/8不是内存泄漏的情况下,JQ不必再次访问DOM或解析页面上的每个DOM元素。这是一个在同一对象上创建100000个不同的单击处理程序实例的示例。当然,它们会被保留,因为它们仍然是活动的单击处理程序。如果您不相信我的话,请在单击处理程序中放置一个警报,运行代码,单击div,然后查看一次单击会出现多少次警报。最初的“MyObject”示例也是错误的。它不会造成内存泄漏。至少在像Chrome这样的现代浏览器中不是这样。现代javascript引擎不单独使用引用计数进行垃圾收集。只要断开对某个根垃圾收集器对象的引用,对象就会被删除。对于纯javascript对象,这只意味着确保没有指向返回全局范围的对象的引用链。当你开始混合DOM对象时,它会变得更复杂一些。关于“MyObject”的例子——它肯定在IE6中泄漏(即使在离开页面之后),在IE7中泄漏,在chrome中也曾经泄漏。就我个人而言,我很高兴浏览器正在发展到一个不再如此的地步