Estoy usando un sistema que necesita inicializar muchos objetos mediante transacciones, y por razones que van más allá del alcance de esta pregunta, estas transacciones deben pasarse a los constructores. De esta manera:¿Los argumentos de constructor obtienen GC'ed?
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)
}
Ahora estoy temiendo que hay un problema con la basura recogida de las transacciones:
val u = new User(new Txn(1234))
System.gc() // hmmm, nothing seems to happen?
Así que mi pregunta es: ¿El argumento t0
constructor consigue siempre el recolector de basura, o puedo crear una fuga de memoria aquí? En un código equivalente Java, yo supongo que tendría algo como esto:
public class User implements Mutable {
final int id;
public User(Txn t0) {
id = t0.newID(this);
}
}
y estoy seguro que se recoge t0
. ¿Pero es esto cierto en el caso Scala?
Si no es así, ¿cómo puedo asegurar que t0
es basura recolectada? Recuerde que I debe pasar en la transacción como un argumento de constructor, porque la clase User
implementa algunos rasgos que deben pasarse a los métodos Txn
, por lo tanto esos métodos (como newID
) no pueden invocarse antes de construir User
.
He intentado antes para construir todo lo que utiliza la transacción fuera del objeto de usuario, con toneladas de val interdependientes lazy
, pero eso fue realmente complicado. Por ejemplo, este, que ya está a medio camino ilegible, produce un desbordamiento de pila:
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))
Usted puede imaginar que es una mierda que el compilador no detectar el problema con el val perezoso falta aquí, así que sin duda prefiera la variante de arg constructor.
¡La comprobación es quizás el mejor consejo! La manera más fácil de verificar es usualmente usar el indicador '-private' en javap, y no preocuparse por bytecode. Esto mostrará primero los campos privados (usando la sintaxis de Java), que incluirá algo como 'x $ 1' para los argumentos del constructor. –