Java Thread Garbage gesammelt oder nicht

85

Diese Frage wurde auf einer Website veröffentlicht. Ich habe dort keine richtigen Antworten gefunden, deshalb poste ich sie hier erneut.

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
    }
}

Bei meiner Abfrage geht es nicht darum, einen Thread zu stoppen. Lassen Sie mich meine Frage umformulieren. Zeile A (siehe Code oben) startet einen neuen Thread. und Zeile B machen die Thread-Referenz null. Die JVM verfügt nun über ein Thread-Objekt (das sich im laufenden Zustand befindet), auf das kein Verweis vorhanden ist (als t = null in Zeile B). Meine Frage ist also, warum dieser Thread (der im Hauptthread keine Referenz mehr hat) so lange läuft, bis der Hauptthread läuft. Nach meinem Verständnis sollte das Thread-Objekt nach Zeile B durch Müll gesammelt worden sein. Ich habe versucht, diesen Code 5 Minuten und länger auszuführen, wobei Java Runtime aufgefordert wurde, GC auszuführen, aber der Thread stoppt einfach nicht.

Hoffe, dass diesmal sowohl der Code als auch die Frage klar sind.

Ashish
quelle

Antworten:

123

Ein laufender Thread wird als sogenannte Garbage Collection-Wurzel betrachtet und ist eines dieser Dinge, die verhindern, dass Dinge gesammelt werden. Wenn der Garbage Collector feststellt, ob Ihr Objekt erreichbar ist oder nicht, verwendet er immer die Garbage Collector-Wurzeln als Referenzpunkte.

Bedenken Sie, warum Ihr Haupt-Thread nicht durch Müll gesammelt wird und niemand auf diesen verweist.

falstro
quelle
16
Diese Antwort wirft derzeit die Frage auf, ob Threads überhaupt GC-fähig sind (nachdem sie beendet wurden). Da diese Frage als Duplikat markiert diese , sollte erwähnt werden , dass Threads nicht mehr als „Garbage Collection roots“ markiert werden , nachdem sie beenden, und damit werden sie für GC erreichbar.
bluenote10
12
Der letzte Satz dringt ein: "Warum ist dein Hauptthema kein Müll ...".
Determinante
Wird ein entsorgter Thread (einer, der verbunden wurde) als Folge nicht mehr als Root betrachtet? Ich bin mir fast sicher, dass die Antwort ja ist, aber ich sehe seltsame Dinge in meiner Bewerbung unter dem Profiler und ich frage mich ...
Groostav
1
Je mehr Antworten ich lese, desto verwirrter werde ich, aber warum der Haupt-Thread keinen Müll sammelt, ist etwas, worüber ich nachdenken muss. Zurück zur Kernfrage: Wenn untergeordnete Threads einige Objekte erstellen, werden sie nicht GCed, da der übergeordnete Thread noch ausgeführt wird und die Referenzen von untergeordneten Threads enthält, selbst wenn sie "ausgegangen" sind (!! ??)
Rahul Kumar
@Groostav Ein verknüpfter Thread wird nicht ausgeführt, es handelt sich also nicht um ein Garbage Collection-Stammverzeichnis. Aber als der übergeordnete Thread aufgerufen wurde child.join(), hatte er einen Verweis auf child. Wenn diese Referenz (und eine andere) nicht verworfen wird, kann der untergeordnete Thread nicht gced werden.
Blaisorblade
23

Wie bereits erläutert, sind laufende Threads per Definition immun gegen GC. Der GC beginnt seine Arbeit mit dem Scannen von "Wurzeln", die als immer erreichbar gelten. Zu den Wurzeln gehören globale Variablen ("statische Felder" in Java-talk) und die Stapel aller laufenden Threads (man kann sich vorstellen, dass der Stapel eines laufenden Threads auf die entsprechende ThreadInstanz verweist ).

Sie können einen Thread jedoch zu einem "Daemon" -Thread machen (siehe Thread.setDaemon(boolean)). Ein Daemon-Thread wird nicht mehr mit Müll gesammelt als ein Nicht-Daemon-Thread, aber die JVM wird beendet, wenn alle laufenden Threads Daemon sind. Eine Möglichkeit, sich das vorzustellen, besteht darin, dass jeder Thread beim Beenden prüft, ob noch einige Nicht-Daemon-Threads ausgeführt werden. Wenn nicht, erzwingt der beendende Thread einen System.exit()Aufruf, der die JVM beendet (wodurch die laufenden Daemon-Threads beendet werden). Dies ist kein GC-Problem. In gewisser Weise werden Threads manuell zugewiesen. Auf diese Weise kann die JVM jedoch Semi-Rogue-Threads tolerieren. Dies wird normalerweise für TimerInstanzen verwendet.

Thomas Pornin
quelle
19

Die JVM hat einen Verweis auf alle laufenden Threads.

Kein Thread (oder die Dinge, auf die er sich bezieht) wird durch Müll gesammelt, solange er noch ausgeführt wird.

Thilo
quelle
13

Der Thread wird nicht durch Müll gesammelt, da Verweise auf die Threads vorhanden sind, die Sie nicht sehen können. Beispielsweise gibt es Referenzen im Laufzeitsystem.

Wenn der Thread erstellt wird, wird er der aktuellen Thread-Gruppe hinzugefügt. Sie können eine Liste der Threads in der aktuellen Thread-Gruppe abrufen. Dies ist eine weitere Möglichkeit, einen Verweis darauf zu erhalten.

vy32
quelle