JavaScript关闭词法环境中的内存泄漏

时间:2015-12-27 13:04:56

标签: javascript memory memory-leaks garbage-collection

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

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);

以下是显示Google Chrome内存增加的时间表:

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);

以下是显示GC正在清理的等效时间线。

No leak

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

1 个答案:

答案 0 :(得分:2)

主要答案是,在您的第二个代码块中,closure1的返回都没有(someMethodouter),outer之外的任何内容都没有他们),所以没有任何东西可以指代创建它们的上下文,并且可以清理上下文。但是,在第二个代码块中,someMethod在返回时仍然存在,作为您分配给aThing的对象的一部分,因此上下文不能用于GC。

让我们按照您的第一个块进行操作:

首次执行outer后,我们(忽略了一堆细节):

            +-------------+
aThing----->| (object #1) |     
            +-------------+     
            | str: ...    |     +--------------------+
            | someMethod  |---->| (context #1)       |
            +-------------+     +--------------------+
                                | something: null    |
                                | closure1: function |
                                +--------------------+
第二次执行后

            +-------------+
aThing----->| (object #2) |     
            +-------------+     
            | str: ...    |     +--------------------+     
            | someMethod  |---->| (context #2)       |     
            +-------------+     +--------------------+     +-------------+                           
                                | something          |---->| (object #1) |                           
                                | closure1: function |     +-------------+                           
                                +--------------------+     | str: ...    |     +--------------------+
                                                           | someMethod  |---->| (context #1)       |
                                                           +-------------+     +--------------------+
                                                                               | something: null    |
                                                                               | closure1: function |
                                                                               +--------------------+
第三次执行后

            +-------------+
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 |
                                                                                                                              +--------------------+

你可以看到它的发展方向。

由于第二个块永远不会保留对closure1someMethod的引用,因此它们都不会将上下文保留在内存中。

我有点惊讶V8(Chrome的JavaScript引擎)没有优化此泄漏,因为只保留someMethodsomeMethod实际上不使用somethingclosure1(或evalnew Functiondebugger),因此虽然理论上它通过上下文引用了它们,但静态分析会显示它们实际上不是使用,所以可以删除。但是闭包优化很容易打扰,我猜这里的东西令人不安。