Javascript jQuery 内存泄漏模式和原因

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5046016/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 15:24:27  来源:igfitidea点击:

jQuery memory leak patterns and causes

javascriptjqueryperformancememory-leaksgarbage-collection

提问by Thimmayya

What are some of the standard issues or coding patterns in jQuery which lead to memory leaks?

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



I have seen a number of questions related to the ajax() call or jsonp or DOM removal on StackOverflow. Most of the jQuery memory leak questions are focussed on specific issues or browsers and it would be nice to have a listing of the standard memory leak patterns in jQuery.

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

Here are some related questions on SO:

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

Resources on the web:

网络资源:

回答by tofarr

From what I understand, memory management in javascript is accomplished by reference counting - while a reference to an object still exists, it will not be deallocated. This means that creating a memory leak in a single page application is trivial, and can trip up those of use coming from a java background. This is not specific to JQuery. Take the following code for example:

据我了解,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();
}

It will look normal until you look at memory usage. Instances of MyObject are never deallocated while the page is active, due to the "_this" pointer (increase the max value of i to see it more dramatically.). (In older versions of IE they were never deallocated until the program exits.) Since javascript objects may be shared between frames (I don't recommend trying this as it is seriously temperamental.), there are cases where even in a modern browser javascript objects can hang around a lot longer than they are meant to.

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

In the context of jquery, references are often stored to save the overhead of dom searching - for example:

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

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

This code will hold on to domObject (and all its contents) forever, because of the reference to it in the callback function.

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

If the writers of jquery have missed instances like this internally, then the library itself will leak, but more often it is the client code.

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

The second example can be fixed by explicitly clearing the pointer when it is no longer required:

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

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

or doing the lookup again:

或再次查找:

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

A good rule of thumb is to be careful where you define your callback functions, and avoid too much nesting where possible.

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

Edit: As was pointed out in the comments by Erik, you could also use the this pointer to avoid the unnescessary dom lookup:

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

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

回答by Dave Methvin

I'll contribute one anti-pattern here, which is the "mid-chain reference" leak.

我将在这里贡献一种反模式,即“中链参考”泄漏。

One of jQuery's strengths is its chaining API, which lets you continue to change, filter, and manipulate the elements:

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

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

At the end of that chain you have a jQuery object with all the ".message .author" elements, but thatobject refers back to and object with the original ".message" elements. You can get to them via the .end()method and do something to them:

在该链的末尾,您有一个包含所有“.message .author”元素的 jQuery 对象,但对象返回到具有原始“.message”元素的对象。你可以通过.end()方法找到他们并对他们做一些事情:

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

Now when used this way there are no problems with leaks. However, if you assign the result of a chain to a variable that has a long life, the back-references to earlier sets stay around and cannot be garbage collected until the variable goes out of scope. If that variable is global, the references never go out of scope.

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

So for example, let's say you read on some 2008 blog post that $("a").find("b")was "more efficient" than $("a b")(even though its not worthy of even thinking about such a micro-optimization). You decide you need a page-wide global to hold a list of authors so you do this:

因此,例如,假设您在 2008 年的一些博客文章中阅读了$("a").find("b")$("a b")(即使它甚至不值得考虑这样的微优化)“更有效”的博客文章。您决定需要一个页面范围的全局变量来保存作者列表,因此您可以这样做:

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

Now you do have a jQuery object with the list of authors, but it also refers back to a jQuery object that is the full list of messages. You probably will never use it or even know it's there, and it's taking up memory.

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

Note that leaks can only occur with the methods that select newelements from an existing set, such as .find, .filter, .childrenetc. The docs indicate when a newset is returned. Simply using a chaining API doesn't cause a leak if the chain has simple non-filtering methods like .css, so this is okay:

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

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