简体   繁体   English

Scala中的闭包内存管理如何工作?

[英]How does the memory management of closures in Scala work?

Scala allows closure like Scala允许关闭

def newCounter = {
  var a=0
  () => {a+=1;a}
}

which defines a function that on every call returns a new independent counter function starting at 1 : 它定义了一个函数,在每次调用时返回一个从1开始的新的独立计数器函数:

scala> val counter1 = newCounter
counter1: () => Int = <function0>

scala> counter1()
res0: Int = 1

scala> counter1()
res1: Int = 2

scala> val counter2 = newCounter
counter2: () => Int = <function0>

scala> counter2()
res2: Int = 1

scala> counter1()
res3: Int = 3

This is quite impressive as usually a would be a representative of a memory address on the stack frame of newCounter. 这是非常令人印象深刻的,因为通常a newCounter的堆栈帧上的内存地址的代表。 I've just read the closure chapter of "Programming in Scala" and it only has the following to say on that matter (p. 155): 我刚刚阅读了“Scala编程”的封闭章节,并且在此事上只有以下内容(第155页):

The Scala compiler rearranges things in cases like this so that the captured parameter lives out on the heap, instead of the stack, and thus can outlive the method call that created it. Scala编译器在这种情况下重新排列事物,以便捕获的参数在堆上而不是堆栈中存在,因此可以比创建它的方法调用更长。 This rearrangement is all taken care of automatically, so you don't have to worry about it. 这种重新安排都是自动完成的,因此您不必担心。

Can anyone elaborate on how this works on byte code level? 任何人都可以详细说明它如何在字节码级别上工作? Is the access similar to a member variable of a class with all the associated synchronization and performance implications? 访问是否类似于具有所有相关同步和性能影响的类的成员变量?

You could use scalac -Xprint:lambdalift <scala-file-name> to investigate this. 您可以使用scalac -Xprint:lambdalift <scala-file-name>来调查此问题。

Your code is actually something like this: 你的代码实际上是这样的:

def newCounter = {
  val a: runtime.IntRef = new runtime.IntRef(0);
  new Function0 {
    private[this] val a$1 = a
    def apply() = {
      a$1.elem = a$1.elem + 1
      a$1.elem
    }
  }
}

There is a wrapper for any var used by lambda. lambda使用的任何var都有一个包装器。 Other vars (not used in closures) are common locale variables. 其他vars (未在闭包中使用)是常见的语言环境变量。

The link to this wrapper is stored as field in the instance of function. 此包装器的链接存储在函数实例中的字段中。

lambdalift in -Xprint:lambdalift is the compiler phase . lambdalift in -Xprint:lambdalift编译阶段 You can get all phases with -Xshow-phases . 您可以使用-Xshow-phases获得所有-Xshow-phases You could use phase number instead of name, it's useful when you are not sure which phase you need. 您可以使用阶段编号而不是名称,当您不确定需要哪个阶段时,它很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM