JavaScript封闭词汇环境中的内存泄漏 [英] JavaScript Memory leak from closure lexical environment

查看:93
本文介绍了JavaScript封闭词汇环境中的内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解为什么下面的代码导致内存泄漏

  var aThing = null; var outer = function(){console.log('running'); var something = aThing; var closure1 = function(){if(something){console.log('something'); }}; aThing = {str:new Array(1000000).join('8'),someMethod:function(){}};}; setInterval(outer,1000);  

a href =https://i.stack.imgur.com/UNgC0.png =nofollow noreferrer>



但是这个代码非常微小,并不会导致内存泄漏:



var aThing = null; var outer = function(){console.log var something = aThing; var closure1 = function(){if(something){console.log('something'); }} aThing = {str:new Array(1000000).join('8')};函数someMethod(){};}; setInterval(outer,1000);

以下是等效的时间表,表明GC正在清理好。



我知道在第一个版本中有一个内存泄漏,因为变量'东西'没有得到清理。为什么它在第二个例子中被GC'ed,而不是第一个?

解决方案

主要答案是,在你的第二个代码块,( closure1 someMethod )都不存在 outer code>( outer 外部没有任何内容指向它们),所以没有任何指向创建它们的上下文的东西,并且可以清除该上下文。作为您分配给 aThing someMethod >,所以上下文不能被GC'd。



让我们来看看你的第一个块发生了什么:



在第一次执行 outer 之后,我们已经(忽略了一大堆细节):

 
+ ------------- +
aThing -----> | (对象#1)|
+ ------------- +
| str:... | + -------------------- +
| someMethod | ----> | (上下文#1)|
+ ------------- + + -------------------- +
| something:null |
| closure1:函数|
+ -------------------- +

second 执行:

 
+ ------------- +
aThing -----> | (对象#2)|
+ ------------- +
| str:... | + -------------------- +
| someMethod | ----> | (上下文#2)|
+ ------------- + + -------------------- + + -------- ----- +
|东西| ----> | (对象#1)|
| closure1:函数| + ------------- +
+ -------------------- + | str:... | + -------------------- +
| someMethod | ----> | (上下文#1)|
+ ------------- + + -------------------- +
| something:null |
| closure1:函数|
+ -------------------- +

third execution:

 
+ ------------- +
aThing -----> | (对象#3)|
+ ------------- +
| str:... | + -------------------- +
| someMethod | ----> | (上下文#3)|
+ ------------- + + -------------------- + + -------- ----- +
|东西| ----> | (对象#2)|
| closure1:函数| + ------------- +
+ -------------------- + | str:... | + -------------------- +
| someMethod | ----> | (上下文#2)|
+ ------------- + + -------------------- + + -------- ----- +
|东西| ----> | (对象#1)|
| closure1:函数| + ------------- +
+ -------------------- + | str:... | + -------------------- +
| someMethod | ----> | (上下文#1)|
+ ------------- + + -------------------- +
| something:null |
| closure1:函数|
+ -------------------- +

您可以



由于第二个块永远不会保留对 closure1 someMethod ,它们都不会将上下文保留在内存中。



我对V8(Chrome的JavaScript引擎)未优化这泄漏了,因为只保留 someMethod ,并且 someMethod 实际上并不使用 something 或 closure1 (或 eval new Function 调试器),所以虽然理论上它通过上下文引用它们,但静态分析表明它们不能实际使用,所以可能是下降。但封闭优化很容易打扰,我猜那里有一些令人不安的东西。


I am trying to understand why the following code causes a memory leak

var aThing = null;
var outer = function() {

    console.log('running');
    var something = aThing;

    var closure1 = function() {
        if (something) {
            console.log('something');
        }
    };

    aThing = {
        str: new Array(1000000).join('8'),
        someMethod: function() {}
    };
};
setInterval(outer, 1000);

Here is the timeline showing memory increasing from Google Chrome:

but this code which is a very slight variation does not cause the same memory leak:

var aThing = null;
var outer = function() {

    console.log('running');
    var something = aThing;
    var closure1 = function() {
        if (something) {
            console.log('something');
        }
    }

    aThing = {
        str: new Array(1000000).join('8')
    };

    function someMethod() {};
};
setInterval(outer, 1000);

Here is the equivalent timeline showing that GC is cleaning up OK.

I understand that in the first version there is a memory leak because the variable 'something' does not get cleaned up. Why is it being GC'ed in the second example but not the first?

解决方案

The primary answer is that in your second code block, neither of the closures (closure1 or someMethod) survives the return of outer (nothing outside outer refers to them), and so there's nothing left that refers to the context where they were created, and that context can be cleaned up. In your second code block, though, someMethod survives the return, as part of the object that you're assigning to aThing, and so the context cannot be GC'd.

Let's follow what happens with your first block:

After the first execution of outer, we have (ignoring a bunch of details):

            +-------------+
aThing----->| (object #1) |     
            +-------------+     
            | str: ...    |     +--------------------+
            | someMethod  |---->| (context #1)       |
            +-------------+     +--------------------+
                                | something: null    |
                                | closure1: function |
                                +--------------------+

after the second execution:

            +-------------+
aThing----->| (object #2) |     
            +-------------+     
            | str: ...    |     +--------------------+     
            | someMethod  |---->| (context #2)       |     
            +-------------+     +--------------------+     +-------------+                           
                                | something          |---->| (object #1) |                           
                                | closure1: function |     +-------------+                           
                                +--------------------+     | str: ...    |     +--------------------+
                                                           | someMethod  |---->| (context #1)       |
                                                           +-------------+     +--------------------+
                                                                               | something: null    |
                                                                               | closure1: function |
                                                                               +--------------------+

after the third execution:

            +-------------+
aThing----->| (object #3) |     
            +-------------+     
            | str: ...    |     +--------------------+     
            | someMethod  |---->| (context #3)       |     
            +-------------+     +--------------------+     +-------------+                                                                          
                                | something          |---->| (object #2) |                                                                          
                                | closure1: function |     +-------------+                                                                          
                                +--------------------+     | str: ...    |     +--------------------+                                               
                                                           | someMethod  |---->| (context #2)       |                                               
                                                           +-------------+     +--------------------+     +-------------+                           
                                                                               | something          |---->| (object #1) |                           
                                                                               | closure1: function |     +-------------+                           
                                                                               +--------------------+     | str: ...    |     +--------------------+
                                                                                                          | someMethod  |---->| (context #1)       |
                                                                                                          +-------------+     +--------------------+
                                                                                                                              | something: null    |
                                                                                                                              | closure1: function |
                                                                                                                              +--------------------+

You can see where this is going.

Since the second block never retains a reference to closure1 or someMethod, neither of them keeps the context in memory.

I'm slightly surprised that V8 (Chrome's JavaScript engine) doesn't optimize this leak away, since only someMethod is retained, and someMethod doesn't actually use something or closure1 (or eval or new Function or debugger), so although in theory it has references to them via the context, static analysis would show that they can't actually be used and so could be dropped. But closure optimization is really easy to disturb, I guess something in there is disturbing it.

这篇关于JavaScript封闭词汇环境中的内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆