不,在这两种情况下,Spark 都不会序列化 Main 对象。方法参数和局部变量(从语义角度来看几乎是同一件事)不“属于”封闭对象或类,它们与特定的方法调用相关联,因此可以直接被闭包捕获。
作为一般规则,如果您需要引用某个对象才能访问某个值,那么该引用将被捕获并因此被序列化:
class Application(n: Int) {
val x = "internal state " + n
def doSomething(ds: Dataset[String], param: String): Unit = {
ds.map(_ + x + param)
}
}
注意这里为了访问x,它是一个instance成员,你必须有封闭的实例可用,因为它取决于实例的实际参数构造与。另一种看待它的方式是记住,当你在上面的例子中使用x时,它实际上是this.x的快捷方式:
ds.map(_ + this.x + param)
与此相比,param 值没有这种依赖关系——它按原样传递给方法,无需访问任何其他封闭对象即可使用它。所以param会被直接捕获并序列化。
这就是为什么建议将实例成员放入局部变量以免捕获整个对象:当您将值放入局部变量时,它不再需要访问封闭的实例:
val localX = this.x
ds.map(_ + localX + param)
当然,如果你有对封闭实例的引用在你想要捕获的对象,像这里:
class Inner(app: Application)
class Application {
val x = new Inner(this)
def doSomething(ds: Dataset[String]): Unit = {
val localX = x
ds.map(_ + localX.toString)
}
}
然后将其存储到局部变量将无济于事,因为 Spark 仍需要序列化 Inner 类的 app 字段,该字段指向 Application 实例。这就是为什么如果您在 Spark 方法中使用的复杂对象图将被发送到执行程序时,您必须小心。