是否在评估按名称块或将闭包存储在堆中时创建了新的堆栈帧?

时间:2017-10-05 12:00:47

标签: scala

假设我有这个方法:

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 =块行执行以及块的确切位置及其相关的闭包数据存储到那一刻。

2 个答案:

答案 0 :(得分:3)

block: => Tblock: 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内联以避免新堆栈帧的成本,但至少对于块的第一次调用它将产生新的堆栈帧。