假设我有这个方法:
def profiledAction[T](block: => T)(implicit driver: WebDriver):(T, Long) = {
val startOfDeletion = System.currentTimeMillis
val result:T = block
val endOfDeletion = System.currentTimeMillis
(result, endOfDeletion - startOfDeletion)
}
我想更好地理解如何根据JVM实现block
的准确传递和评估。据我所知,by-name参数可能有一些与其范围相关的数据,我们称之为闭包。
我认为闭包存储在堆中并且没有创建新的堆栈框架,但是你可以澄清我的理解是否正确吗?
换句话说:我理解在使用by-value参数调用新方法时如何分配堆栈帧。在这里,我试图更好地理解val结果中究竟发生了什么:T =块行执行以及块的确切位置及其相关的闭包数据存储到那一刻。
答案 0 :(得分:3)
block: => T
是block: Function0[T]
的语法糖。创建类型Function0
的变量并通过(通过引用)传递任何其他参数的方式。
然后,在函数内部,当您执行val result = block
时,将创建一个新的堆栈帧,执行该函数,返回值从堆栈弹出并分配给result
答案 1 :(得分:1)
为了更好地理解名称参数及其对堆栈的影响,首先让我们检查一下编译时名称参数的实际发生情况。
鉴于这个非常简化的代码:
def main(args: Array[String]): Unit = {
byName(1)
}
def byName(a: => Int): Int = {
a
}
使用-Xprint:jvm
标志进行编译,我们会看到:
[[syntax trees at end of jvm]] // SOTesting.scala
package com.testing {
object SOTesting extends Object {
def main(args: Array[String]): Unit = {
SOTesting.this.byName({
$anonfun()
});
()
};
def byName(a: Function0): Int = a.apply$mcI$sp();
final <static> <artifact> def $anonfun$main$1(): Int = 1;
def <init>(): com.testing.SOTesting.type = {
SOTesting.super.<init>();
()
}
}
}
按名称参数只是Function0
的语法糖。
现在回到你的问题:
我试图更好地理解
val result: T = block
行执行中究竟发生了什么,以及在那个时刻存储了与块及其相关的闭包数据的确切位置。
这是对Function0.apply
的方法调用。注意,这可以通过JIT内联以避免新堆栈帧的成本,但至少对于块的第一次调用它将产生新的堆栈帧。