如何在Javascript中为变量分配内存?

sub*_*bbu 44 javascript memory-management

我想知道局部变量如何在javascript中分配内存.在C和C++中,局部变量存储在堆栈中.它在javascript中是一样的吗?或者一切都存储在堆中?

T.J*_*der 54

它实际上是一个非常有趣的Javascript领域.规范中的细节,但是:Javascript处理局部变量的方式与C的方式完全不同.当你调用一个函数时,除此之外还会创建一个用于该调用的"变量环境",它具有一个称为"绑定对象"的东西.(简称为"变量对象";说"变量环境的绑定对象"只是有点啰嗦!)变量对象具有函数参数的属性,函数中声明的所有局部变量,以及在函数内声明的所有函数(以及其他一些东西).首先根据变量对象检查函数中的非限定引用(例如,ain foo,not foo),以查看它们是否与其匹配; 如果他们这样做,那么使用这些属性.

当一个闭包在函数返回时幸存(这可能由于几个原因而发生),该函数调用的变量对象由闭包引用保留在内存中.乍一看,这表明堆栈不用于局部变量; 实际上,现代JavaScript引擎非常智能,并且可能(如果它值得)将堆栈用于闭包实际上没有使用的本地.(当然,堆栈仍然用于跟踪返回地址等.)

这是一个例子:

function foo(a, b) {
    var c;

    c = a + b;

    function bar(d) {
        alert("d * c = " + (d * c));
    }

    return bar;
}

var b = foo(1, 2);
b(3); // alerts "d * c = 9"
Run Code Online (Sandbox Code Playgroud)

当我们调用时foo,使用以下属性创建变量对象:

  • obj.foofoo - 函数的参数
  • a - 函数中声明的局部变量
  • b - 在函数内声明的函数
  • (......和其他一些事情)

c执行语句bar,它的引用foo,c = a + b;以及c该呼叫到变量对象的属性a.当b返回对其中foo声明的函数的引用时,foo继续调用bar返回.由于bar对该特定调用具有(隐藏)变量对象的引用foo,因此变量对象存活(而在正常情况下,它将没有未完成的引用,因此可用于垃圾收集).

之后,当我们调用时bar,会创建一个用于该调用的变量对象(其中包括)一个名为foo - 的参数bar.d首先根据该调用的变量对象检查内部的非限定引用; 例如,bar解析为bar变量对象上的属性以进行调用bar.但是,然后根据"范围链"中的下一个变量对象检查与其变量对象上的属性不匹配的非限定引用,该变量对象是bar调用的变量对象d.因为它有一个属性d,这就是其中使用的属性bar.例如,粗略地说:

+???????????????????????????+
|   global binding object   |
+???????????????????????????+
| ....                      |
+???????????????????????????+
             ^
             | chain
             |
+???????????????????????????+
| `foo` call binding object |
+???????????????????????????+
| a = 1                     |
| b = 2                     |
| c = 3                     |
| bar = (function)          |
+???????????????????????????+
             ^
             | chain
             |
+???????????????????????????+
| `bar` call binding object |
+???????????????????????????+
| d = 3                     |
+???????????????????????????+

实现可以自由使用任何他们想要的机制在幕后作出上述似乎发生.对于函数调用,不可能直接访问变量对象,并且规范明确指出,如果变量对象只是一个概念,而不是实现的文字部分,那就完全没问题了.一个简单的实现可能只是字面上做的规范说; 当没有涉及闭包时(为了速度利益),更复杂的一个可以使用堆栈,或者可以总是使用堆栈但是在弹出堆栈时"撕掉"闭包所需的变量对象.在任何特定情况下,唯一知道的方法是查看他们的代码.:-)

有关闭包,范围链等的更多信息,请点击此处:

  • 谢谢。终于明白闭包了。 (2认同)

jJ'*_*jJ' 22

不幸的是,答案是:这取决于.

最近的javascript引擎发生了重大转变,开始优化比以前更好.答案曾经是:"局部变量存储在堆分配的堆栈帧中,以便闭包工作".它不再那么简单了.

对于Scheme实现和闭包优化已经(或者曾经是20 - 30年前)的研究(JavaScript继承了很多Scheme闭包,除了让它更复杂的延续).

我没有准备好纸质链接,但是如果你没有非常高效的垃圾收集器,你也需要使用堆栈.棘手的部分是处理闭包,闭包需要堆分配的变量.因为使用了不同的策略.结果是混合,其中:

  • 通过内联函数,您可以显着减少分配/解除分配的堆分配帧数
  • 一些变量可以安全地放在堆栈上,因为它的时间跨度是有限的(它通常也连接到内联函数调用)
  • 在某些情况下,您知道您可能正在创建闭包,但您可以等到发生这种情况,然后为其分配堆栈帧并从堆栈复制当前值
  • 有一些优化连接到尾部调用,你可以在之前进行堆分配,然后重复使用堆栈帧进行下一个函数调用,但据我所知,这在javascript引擎中没有使用

这个领域在几个竞争引擎中的变化非常快,所以答案可能仍然是"它取决于"

此外,在该语言的新版本中,我们将看到类似的功能let,const这实际上使引擎更容易优化分配决策.特别是不变性非常有用,因为您可以在堆栈中自由复制值(例如,然后将闭包对象的一部分组成),而无需解决来自不同闭包的变化变量的冲突.

  • 就个人而言,对我来说最有影响力的是计划专家Kent Dybvig的论文http://www.cs.unm.edu/~williams/cs491/three-imp.pdf,还有一些更专业/详细的论文建立在顶部它的.另外,我最近看到了许多有趣的东西,描述了当前的JavaScript引擎以及团队正在制作的进展,如http://wingolog.org/archives/2011/07/05/v8-a-tale-of-two - 编译器但它们通常不会太深. (2认同)