Java Thread Garbage recolectado o no

85

Esta pregunta se publicó en algún sitio. No encontré las respuestas correctas allí, así que lo estoy publicando aquí nuevamente.

public class TestThread {
    public static void main(String[] s) {
        // anonymous class extends Thread
        Thread t = new Thread() {
            public void run() {
                // infinite loop
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                    // as long as this line printed out, you know it is alive.
                    System.out.println("thread is running...");
                }
            }
        };
        t.start(); // Line A
        t = null; // Line B
        // no more references for Thread t
        // another infinite loop
        while (true) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
            }
            System.gc();
            System.out.println("Executed System.gc()");
        } // The program will run forever until you use ^C to stop it
    }
}

Mi consulta no se trata de detener un hilo. Permítame rehacer mi pregunta. La línea A (ver código arriba) inicia un nuevo hilo; y la línea B hacen nula la referencia del hilo. Entonces, la JVM ahora tiene un objeto Thread (que está en estado de ejecución) al que no existe ninguna referencia (como t = nulo en la línea B). Entonces mi pregunta es, ¿por qué este hilo (que ya no tiene referencia en el hilo principal) sigue ejecutándose hasta que el hilo principal se está ejecutando? Según tengo entendido, el objeto del hilo debería haber sido recolectado como basura después de la Línea B. Intenté ejecutar este código durante 5 minutos y más, solicitando Java Runtime para ejecutar GC, pero el hilo simplemente no se detiene.

Espero que tanto el código como la pregunta estén claros esta vez.

Ashish
fuente

Respuestas:

123

Un hilo en ejecución se considera una raíz de recolección de basura y es una de esas cosas que evita que las cosas se recolecten. Cuando el recolector de basura determina si su objeto es ' accesible ' o no, siempre lo hace utilizando el conjunto de raíces del recolector de basura como puntos de referencia.

Considere esto, ¿por qué su hilo principal no se recolecta como basura? Nadie hace referencia a ese tampoco.

falstro
fuente
16
Esta respuesta, tal como está actualmente, plantea la pregunta de si los subprocesos pueden ser GC'ed (después de que terminan). Dado que esta pregunta está marcada como un duplicado de esta , debe mencionarse que los subprocesos ya no se marcarán como "raíces de recolección de basura" después de que terminen y, por lo tanto, serán accesibles para GC.
bluenote10
13
La última frase es penetrante: "por qué tu hilo principal no es basura ...".
Determinante
Entonces, como seguimiento, ¿un hilo eliminado (uno que se ha unido) ya no se considerará una raíz? Estoy casi seguro de que la respuesta es sí, pero veo cosas extrañas en mi aplicación en el generador de perfiles y me hace preguntarme ...
Groostav
1
Cuantas más respuestas leo, más confuso me siento, pero sí, por qué el hilo principal no es recolectar basura es algo sobre lo que reflexionar. Pero volviendo a la pregunta principal, si los subprocesos secundarios crean algunos objetos, no se recibirán GC, ya que el subproceso principal todavía se está ejecutando y contiene las referencias de los subprocesos secundarios incluso si se han agotado (!! ??)
Rahul Kumar
@Groostav Un subproceso que se ha unido no se está ejecutando, por lo que no es una raíz de recolección de basura. Pero cuando el hilo principal llamó child.join(), tenía una referencia a child. Si esa referencia (y cualquier otra) no se descarta, el hilo secundario no puede ser GCed.
Blaisorblade
23

Como se explicó, los subprocesos en ejecución son, por definición, inmunes a GC. El GC comienza su trabajo escaneando las "raíces", que se consideran siempre accesibles; las raíces incluyen variables globales ("campos estáticos" en Java-talk) y las pilas de todos los subprocesos en ejecución (se puede imaginar que la pila de un subproceso en ejecución hace referencia a la Threadinstancia correspondiente ).

Sin embargo, puede convertir un hilo en un hilo "demonio" (ver Thread.setDaemon(boolean)). Un subproceso daemon no se recolecta más basura que un subproceso que no es demonio, pero la JVM sale cuando todos los subprocesos en ejecución son demonios. Una forma de imaginarlo es que cada hilo, cuando termina, comprueba si quedan algunos hilos en ejecución que no sean demonios; si no, el hilo de terminación fuerza una System.exit()llamada, que sale de la JVM (matando los hilos del demonio en ejecución). Este no es un problema relacionado con GC; en cierto modo, los subprocesos se asignan manualmente. Sin embargo, así es como la JVM puede tolerar subprocesos semi-maliciosos. Suele utilizarse para Timerinstancias.

Thomas Pornin
fuente
19

La JVM tiene una referencia a todos los subprocesos en ejecución.

Ningún hilo (o las cosas a las que se refiere) se recolectará como basura mientras aún se esté ejecutando.

Thilo
fuente
13

El subproceso no se recolecta como basura porque hay referencias a los subprocesos que no puede ver. Por ejemplo, hay referencias en el sistema de ejecución.

Cuando se crea el hilo, se agrega al grupo de hilos actual. Puede obtener una lista de subprocesos en el grupo de subprocesos actual, por lo que es otra forma de obtener una referencia a él.

vy32
fuente