I am using a system which needs to initialize many objects using transactions, and for reasons beyond the scope of this question these transactions must be passed into the constructors. Like this:
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)
}
Now I am fearing there is a problem with garbage collecting the transactions:
val u = new User(new Txn(1234))
System.gc() // hmmm, nothing seems to happen?
So my question is: Does the t0 constructor argument ever get garbage collected, or do I create a memory leak here? In an equivalent Java code, I guess I’d have something like this:
public class User implements Mutable {
final int id;
public User(Txn t0) {
id = t0.newID(this);
}
}
and I am sure t0 is collected. But is this true in the Scala case?
If not, how can I ensure t0 is garbage collected? Remember that I must pass in the transaction as a constructor argument, because the User class implements some traits which must be passed into Txn‘s methods, thus those methods (like newID) cannot be called before constructing User.
I have tried before to construct everything that uses the transaction outside of the user object, with tons of lazy interdependent vals, but that was really messy. For example, this, which is already halfway unreadable, produces a stack overflow:
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))
You can imagine that it really sucks that the compiler won’t spot the problem with the missing lazy val here, so I would definitely prefer the constructor arg variant.
If absolutely necessary, I recommend you use
javapto see what the class has compiled into. Some rules to avoid getting the constructor argument turned into a class parameter:deforlazy val.val (a, b) = f(x)).valorvar.