Scala 构造函数参数是否获得GC';预计起飞时间?
我使用的系统需要使用事务初始化许多对象,并且由于超出本问题范围的原因,必须将这些事务传递给构造函数。像这样:Scala 构造函数参数是否获得GC';预计起飞时间?,scala,constructor,garbage-collection,Scala,Constructor,Garbage Collection,我使用的系统需要使用事务初始化许多对象,并且由于超出本问题范围的原因,必须将这些事务传递给构造函数。像这样: trait Mutable class Txn(i: Int) { def newID(implicit m: Mutable): Int = i override def finalize(): Unit = println("Finalised " + i) } class User(t0: Txn) extends Mutable { val id = t0.new
trait Mutable
class Txn(i: Int) {
def newID(implicit m: Mutable): Int = i
override def finalize(): Unit = println("Finalised " + i)
}
class User(t0: Txn) extends Mutable {
val id = t0.newID(this)
}
现在我担心垃圾收集事务会出现问题:
val u = new User(new Txn(1234))
System.gc() // hmmm, nothing seems to happen?
所以我的问题是:t0
constructor参数是否被垃圾收集,或者我是否在这里创建了内存泄漏?在一个等效的Java代码中,我想我会有如下内容:
public class User implements Mutable {
final int id;
public User(Txn t0) {
id = t0.newID(this);
}
}
我确信t0
已收集。但在Scala的案例中这是真的吗
如果没有,我如何确保t0
被垃圾收集?请记住,我必须将事务作为构造函数参数传入,因为User
类实现了一些必须传递到Txn
方法中的特性,因此在构造User
之前不能调用这些方法(如newID
)
我以前尝试过在用户对象之外构造所有使用事务的东西,使用大量的lazy
相互依赖的VAL,但那真的很混乱。例如,这已经有一半无法读取,会产生堆栈溢出:
trait User extends Mutable { def id: Int }
def newUser(implicit tx: Txn): User = {
lazy val _id: Int = tx.newID(u)
lazy val u = new User { val id: Int = _id } // oops, should be lazy val id!
u
}
val u = newUser(new Txn(1234))
您可以想象,编译器不会在这里发现缺少的惰性val的问题,这真的很糟糕,因此我肯定更喜欢构造函数arg变量。我曾经遇到的问题似乎是在一个特定的情况下,
System.gc()
不会立即生效
但是我可以观察到最终结果发生在一个普通的REPL中,也发生在一些编译代码中,所以我猜答案是,‘是的,构造函数参数确实会被垃圾收集’
当通过超级类时,它也会起作用,这是另一个好消息:
abstract class Underlying(t0: Txn) extends Mutable {
val id1 = t0.newID(this)
}
class User(t0: Txn) extends Underlying(t0) {
val id2 = t0.newID(this)
}
如果构造函数参数未在静态初始值设定项之外使用,则会得到GCed。您可以检查字节码,并验证在这种情况下没有保留对构造函数参数的引用
class WillNotStore(s: Seq[Int]) { val length = s.length }
public WillNotStore(scala.collection.Seq);
Code:
0: aload_0
1: invokespecial #18; //Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: invokeinterface #22, 1; //InterfaceMethod scala/collection/SeqLike.length:()I
11: putfield #11; //Field length:I
14: return
类WillNotStore(s:Seq[Int]){val length=s.length}
公共WillNotStore(scala.collection.Seq);
代码:
0:aload_0
1:特别是#18//方法java/lang/Object。“:()V
4:aload_0
5:aload_1
6:调用接口#22,1//接口方法scala/collection/SeqLike.长度:()I
11:putfield#11//字段长度:I
14:返回
请注意,参数已加载(第5行)并在其上调用了一个方法(第6行),但在构造函数退出(第14行)之前只存储了答案(第11行)。如果绝对必要,我建议您使用
javap
查看类编译成了什么。避免将构造函数参数转换为类参数的一些规则:
- 不要在
或def
上使用它lazy val
- 不要在模式匹配的赋值中使用它(比如
)val(a,b)=f(x)
- 当然,不要将其声明为
或val
var
scala-cp/path/to/tools.jar
+:javap-c WillNotStore
只需几秒钟……@Sciss-Daniel的答案更完整;甚至在初始值设定项中的模式匹配也是一个奇怪的特例,我已经忘记了DelayedInit
是另一种方法(尽管它在那里更明显是必要的,因为它在概念上类似于lazy val
),检查可能是最好的建议!最简单的检查方法通常是在javap中使用-private
标志,而不用担心字节码。这将首先列出私有字段(使用Java语法),其中将包含类似于构造函数参数的x$1
。