构造函数参数是否得到GC?

0__*_*0__ 5 constructor garbage-collection scala

我正在使用一个需要使用事务初始化许多对象的系统,并且出于超出此问题范围的原因,这些事务必须传递给构造函数.像这样:

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)
}
Run Code Online (Sandbox Code Playgroud)

现在我担心垃圾收集交易存在问题:

val u = new User(new Txn(1234))
System.gc()  // hmmm, nothing seems to happen?
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:t0构造函数参数是否会被垃圾收集,或者我是否在这里创建内存泄漏?在等效的Java代码中,我想我会有这样的事情:

public class User implements Mutable {
    final int id;
    public User(Txn t0) {
        id = t0.newID(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

我肯定t0是收集的.但在Scala案件中这是真的吗?

如果没有,我怎样才能确保t0收集垃圾?请记住,我必须将事务作为构造函数参数传递,因为User该类实现了一些必须传递给Txn方法的特性,因此newID在构造之前不能调用这些方法(如)User.

我之前已经尝试构建在用户对象之外使用事务的所有事务,具有大量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))
Run Code Online (Sandbox Code Playgroud)

你可以想象,编译器在这里找不到丢失的延迟val的问题真的很糟糕,所以我肯定更喜欢构造函数arg变种.

Rex*_*err 6

如果构造函数参数未在静态初始值设定项之外使用,则它们将获得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
Run Code Online (Sandbox Code Playgroud)

请注意,加载了参数(第5行)并在其上调用了一个方法(第6行),但在构造函数退出之前只存储了答案(第11行)(第14行).


Dan*_*ral 5

如果绝对必要,我建议您使用javap以查看该类编译的内容.避免获取构造函数参数的一些规则变为类参数:

  • 不要在def或上使用它lazy val.
  • 不要在进行模式匹配的任务中使用它(比如val (a, b) = f(x)).
  • 当然,不要将其声明为valvar.