jQuery内存泄漏模式和原因

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


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

这里有一些关于SO的相关问题:

  • 为什么jQuery泄漏内存如此糟糕?
  • 简单的jQuery Ajax调用在Internet Explorer中泄漏内存
  • 涉及jQuery Ajax请求的内存泄漏

networking资源:

  • 如何使用jQuery.data将对象和数据附加到DOM,以避免内存泄漏问题
  • JavaScript中的内存泄漏模式

据我所知,在javascript中的内存pipe理是通过引用计数完成的 – 虽然对对象的引用依然存在,但不会被释放。 这意味着在一个页面应用程序中创build一个内存泄漏是微不足道的,并且可能导致来自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”指针,MyObject的实例在页面处于活动状态时不会解除分配(增加i的最大值以使其更显着)。 (在老版本的IE浏览器中,他们从来没有被释放,直到程序退出。)因为JavaScript对象可以在框架之间共享(我不build议尝试这个,因为它是严重的气质)。有时甚至在现代浏览器的JavaScript对象可能比他们想要的要长很多。

在jQuery的上下文中,引用通常被存储以节省domsearch的开销 – 例如:

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

这段代码将永远保留在domObject(及其所有内容)上,因为在callback函数中引用了它。

如果jquery的作者在内部错过了这样的实例,那么库本身就会泄漏,但更多的时候是客户端代码。

第二个示例可以通过在不再需要指针时明确地清除指针来解决:

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

或者再次查找:

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

一个好的经验法则是在定义callback函数的地方要小心,并尽可能避免太多的嵌套。

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

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

我会在这里贡献一个反模式,这是“中链参考”漏洞。

jQuery的优势之一是它的链接API,它可以让你继续改变,过滤和操作元素:

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

在这个链的最后,你有一个包含所有“.message .author”元素的jQuery对象,但是这个对象引用了原始的“.message”元素,并将其作为对象。 你可以通过.end()方法find他们,并对他们做些什么:

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

现在使用这种方式,没有泄漏问题。 但是,如果将链的结果赋予具有较长生命周期的variables,则对较早集的反向引用将保留在周围,并且不能进行垃圾回收,直到该variables超出范围。 如果该variables是全局的,那么引用永远不会超出范围。

所以举个例子,假设你在2008年的一篇博文中看到$("a").find("b")$("ab")更有效率(即使它不值得甚至考虑这样的微优化)。 你决定你需要一个页面范围的全局来保存一个作者列表,所以你这样做:

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

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

请注意,只有在从现有集合中select元素(例如.find.filter.filter等)的方法才会发生泄漏。文档指示何时返回集合。 简单地使用链接API不会导致泄漏,如果链有简单的非过滤方法,如.css ,所以这是好的:

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