2011-12-27 31 views
5

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.

Respuesta

5

Si es absolutamente necesario, le recomiendo que use javap para ver en qué se ha compilado la clase. Algunas reglas para evitar conseguir el argumento del constructor convertido en un parámetro de clase:

  • No lo utilice en def o lazy val.
  • No lo utilice en tareas que coincidan con el patrón (como val (a, b) = f(x)).
  • Y, por supuesto, no lo declare como val o var.
+1

¡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. –

0

El problema me parecía que estaba en una situación particular en la que System.gc() no entraba en acción de inmediato.

Pero puedo observar que la finalización tiene lugar en un REPL simple, y también en un código compilado, así que supongo que la respuesta es 'sí, el argumento constructor obtiene basura recolectada'.

También funciona cuando pasa a través de clases súper, lo que es aún más buenas noticias:

abstract class Underlying(t0: Txn) extends Mutable { 
    val id1 = t0.newID(this) 
} 

class User(t0: Txn) extends Underlying(t0) { 
    val id2 = t0.newID(this) 
} 
6

argumentos de constructor consiguen GCed si no se utilizan fuera del inicializador estático. Puede verificar el bytecode y verificar que no se preserve ninguna referencia al argumento constructor en este caso.

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 

Nota que se carga el argumento (línea 5) y un método se denomina en ella (línea 6), pero sólo la respuesta se almacena (línea 11) antes de que el constructor se cierra (línea 14).

+0

¡Hah! Le acabo de decir que use javap, y aquí lo está usando para responder su pregunta. :-) –

+0

@ DanielC.Sobral - 'scala -cp/path/to/tools.jar' +': javap -c WillNotStore' solo toma unos segundos .... –

+0

Gracias Rex, esto debería obtener un segundo aceptar:) –

Cuestiones relacionadas