jdk - limpiar basura en java
¿Un hilo en ejecución en un objeto evita que se recolecte basura en java? (6)
Estoy dispuesto a apostar que la JVM incluye una referencia a cada objeto de subproceso que está activo o puede programarse en su conjunto raíz, pero no tengo la especificación conmigo para confirmar esto.
Dado el código:
new Thread(new BackgroundWorker()).start();
Intuitivamente, parece que la instancia de BackgroundWorker debe estar a salvo de GC hasta que el hilo salga, pero ¿es así? Y por qué ?
Editar:
Todo este calor es básicamente generado por mí al hacer al menos dos preguntas diferentes en la misma publicación. La pregunta en el título tiene una respuesta, la muestra del código lleva en una dirección diferente, con dos posibles resultados dependiendo de la alineación.
Las respuestas publicadas son realmente excelentes. Voy a otorgar a Software Monkey la casilla de verificación verde. Tenga en cuenta que la respuesta de Darron es igualmente válida, pero Software Monkey explicó el problema que estaba teniendo; fue la respuesta que funcionó para mí.
Gracias a todos por hacer de esto una aventura memorable;)
No, no creo que no sea seguro.
En la práctica, casi con seguridad saldrás con la tuya. Sin embargo, el Modelo de Memoria Java es sorprendente. De hecho, solo hubo una discusión de la semana pasada en la lista de correo de JMM sobre los planes para agregar un método para "mantener vivos los objetos". Actualmente, el finalizador puede ejecutarse sin una relación de pase previo antes de la ejecución de un método miembro. Por el momento, técnicamente es necesario introducir un realismo de pasar antes de sincronizar por todos lados o escribir algo volátil al final de cada método y una lectura de ese volátil en el finalizador.
Como señala Darron, si puede obtener el objeto Thread
(a través de Thread.enumerate
por ejemplo), puede run
on it, que llama la Runnable
de Runnable
. Sin embargo, todavía no creo que haya sucedido, antes allí.
Mi consejo: no intentes ser demasiado "inteligente".
Sí, porque el Thread
mantiene una referencia interna Runnable
(después de todo, tiene que saber qué ejecutar ).
Si, es seguro. La razón por la cual no es tan obvio como podrías pensar
El hecho de que el código en BackgroundWorker se esté ejecutando no lo hace seguro: el código en cuestión puede que no haga referencia a ningún miembro de la instancia actual, lo que permite optimizar "this".
Sin embargo, si lee cuidadosamente la especificación para el método run () de la clase java.lang.Thread, verá que el objeto Thread debe mantener una referencia a Runnable para cumplir con su contrato.
EDITAR: porque he sido rechazado varias veces en esta respuesta, voy a ampliar mi explicación.
Supongamos por el momento que no era legal para nadie excepto Thread para llamar a Thread.run (),
En ese caso, sería legal que la implementación predeterminada de Thread.run () se vea así:
void run() {
Runnable tmp = this.myRunnable; // Assume JIT make this a register variable.
this.myRunnable = null; // Release for GC.
if (tmp != null)
tmp.run(); // If the code inside tmp.run() overwrites the register, GC can occur.
}
Lo que sigo diciendo es que nada en el JLS evita que un objeto sea recolectado solo porque un hilo está ejecutando un método de instancia. Esto es parte de lo que hace que corregir la finalización sea tan difícil.
Para detalles insoportables sobre esto de personas que lo entienden mucho mejor que yo, vea este hilo de discusión de la lista de interés de concurrencia.
Es seguro. La JVM contiene una referencia a cada hilo. El subproceso se aferra a una instancia del ejecutable pasado a su constructor. Por lo tanto, Runnable es muy accesible y no se recogerá durante la vida del hilo.
Sabemos que el subproceso contiene una referencia al ejecutable debido al javadoc para Thread.run () :
Si este subproceso se construyó utilizando un objeto Runnable separado, entonces se llama al método de ejecución del objeto Runnable; de lo contrario, este método no hace nada y regresa.
Sí, porque GC solo puede recopilar objetos no alcanzables por ningún hilo, y el hilo debe contener una referencia a su ejecución (o no podría invocarlo). Así que, claramente, su objeto Runnable es alcanzable mientras su hilo se está ejecutando.
Independientemente de la semántica requerida para la ejecución , su objeto no será GC hasta que ya no sea alcanzable por este nuevo hilo o cualquier otro; eso será al menos lo suficientemente largo como para invocar la ejecución de Runnable (), y durante toda la vida del hilo si ese hilo es capaz de llegar a la instancia de Runnable, por lo que su construcción está garantizada por la especificación de JVM.
EDITAR: Debido a que Darron está golpeando hasta la muerte, y algunos parecen convencidos por su argumento, voy a ampliar mi explicación, basada en la suya.
Supongamos por el momento que no era legal para nadie excepto Thread para llamar a Thread.run (),
En ese caso, sería legal que la implementación predeterminada de Thread.run () se vea así:
void run() {
Runnable tmp = this.myRunnable; // Assume JIT make this a register variable.
this.myRunnable = null; // Release for GC.
if(tmp != null) {
tmp.run(); // If the code inside tmp.run() overwrites the register, GC can occur.
}
}
Sostengo que en este caso tmp sigue siendo una referencia al ejecutable alcanzable por el subproceso que se ejecuta dentro de Thread.run () y, por lo tanto, no es elegible para GC.
¿Qué pasa si (por alguna razón inexplicable) el código se ve así:
void run() {
Runnable tmp = this.myRunnable; // Assume JIT make this a register variable.
this.myRunnable = null; // Release for GC.
if(tmp != null) {
tmp.run(); // If the code inside tmp.run() overwrites the register, GC can occur.
System.out.println("Executed runnable: "+tmp.hashCode());
}
}
Claramente, la instancia referida por tmp no puede ser GC''d mientras tmp.run () se está ejecutando.
Creo que Darron cree erróneamente que alcanzable significa solo las referencias que se pueden encontrar al perseguir las referencias de instancia comenzando con todas las instancias de subprocesos como raíces, en lugar de definirse como una referencia que puede ser vista por cualquier subproceso que se ejecute. O eso, o estoy equivocado al creer lo opuesto.
Además, Darron puede suponer que el compilador de JIT realiza los cambios que desea: el compilador no puede cambiar la semántica referencial del código de ejecución. Si escribo código que tiene una referencia accesible, el compilador no puede optimizar esa referencia y hacer que mi objeto se recopile mientras esa referencia está dentro del alcance.
No conozco el detalle de cómo se encuentran realmente los objetos alcanzables; Solo estoy extrapolando la lógica que creo que debe contener. Si mi razonamiento no fuera correcto, cualquier objeto instanciado dentro de un método y asignado solo a una variable local en ese método sería inmediatamente elegible para GC: claramente esto no es así ni puede serlo.
Además, todo el debate es discutible. Si la única referencia alcanzable está en el método Thread.run (), porque la ejecución del ejecutable no hace referencia a su instancia y no existe otra referencia a la instancia, incluido el implícito, esto pasó al método run () (en bytecode, no como un argumento declarado), entonces no importa si se recopila la instancia del objeto; al hacerlo, por definición, no puede causar daños ya que no es necesario ejecutar el código si lo implícito ha sido optimizado . Siendo ese el caso, incluso si Darron está en lo correcto, el resultado práctico final es que la construcción postulada por el OP es perfectamente segura. De cualquier manera. No importa. Permítanme repetir eso una vez más, solo para ser claros: en el análisis final, no importa .