Java Thread Garbage collecté ou non

85

Cette question a été publiée sur un site. Je n'ai pas trouvé de bonnes réponses ici, alors je la poste à nouveau ici.

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

Ma requête ne concerne pas l'arrêt d'un thread. Permettez-moi de reformuler ma question. La ligne A (voir code ci-dessus) démarre un nouveau thread; et la ligne B rendent la référence de thread nulle. Ainsi, la JVM a maintenant un objet Thread (qui est en cours d'exécution) auquel aucune référence n'existe (comme t = null sur la ligne B). Ma question est donc de savoir pourquoi ce thread (qui n'a plus de référence dans le thread principal) continue de fonctionner jusqu'à ce que le thread principal soit en cours d'exécution. D'après ce que j'ai compris, l'objet thread aurait dû être récupéré après la ligne B. J'ai essayé d'exécuter ce code pendant 5 minutes et plus, demandant à Java Runtime d'exécuter GC, mais le thread ne s'arrête tout simplement pas.

J'espère que le code et la question sont clairs cette fois.

Ashish
la source

Réponses:

123

Un thread en cours d'exécution est considéré comme une soi-disant racine de garbage collection et est l'une de ces choses qui empêchent le garbage collection. Lorsque le garbage collector détermine si votre objet est « accessible » ou non, il le fait toujours en utilisant l'ensemble des racines du garbage collector comme points de référence.

Considérez ceci, pourquoi votre thread principal n'est-il pas garbage collection, personne ne fait référence à celui-là non plus.

falstro
la source
16
Cette réponse, telle qu'elle est actuellement, soulève la question de savoir si les threads peuvent être GC (après qu'ils se terminent). Puisque cette question est marquée comme un duplicata de celle-ci , il convient de mentionner que les threads ne seront plus marqués comme "racines de ramasse-miettes" après leur terminaison, et ainsi, ils deviennent accessibles pour GC.
bluenote10
13
La dernière phrase est pénétrante: "pourquoi votre thread principal n'est-il pas une poubelle ...".
Déterminant
Donc, comme suivi, un thread supprimé (celui qui a été joint) ne sera-t-il plus considéré comme une racine? Je suis presque certain que la réponse est oui, mais je vois des choses étranges sur mon application sous le profileur et cela m'a fait me demander ...
Groostav
1
Plus je lis de réponses, plus je suis confus, mais oui, pourquoi le thread principal ne reçoit pas les déchets collectés est quelque chose à méditer. Mais pour revenir à la question fondamentale, si les threads enfants créent des objets, ils ne seront pas GC car le thread parent est toujours en cours d'exécution et contient les références des threads enfants même s'ils sont `` épuisés '' (!! ??)
Rahul Kumar
@Groostav Un thread qui a été joint n'est pas en cours d'exécution, il ne s'agit donc pas d'une racine de garbage collection. Mais lorsque le thread parent a appelé child.join(), il avait une référence à child. Si cette référence (et toute autre) n'est pas ignorée, le thread enfant ne peut pas être GC.
Blaisorblade
23

Comme expliqué, les threads en cours d'exécution sont, par définition, immunisés contre GC. Le GC commence son travail en scannant les «racines», qui sont réputées toujours joignables; les racines incluent des variables globales ("champs statiques" dans Java-talk) et les piles de tous les threads en cours d'exécution (on peut imaginer que la pile d'un thread en cours d'exécution référence l' Threadinstance correspondante ).

Cependant, vous pouvez faire d'un thread un thread "démon" (voir Thread.setDaemon(boolean)). Un thread démon n'est pas plus récupéré qu'un thread non démon, mais la JVM se ferme lorsque tous les threads en cours d'exécution sont démon. Une façon de l'imaginer est que chaque thread, lorsqu'il se termine, vérifie s'il reste des threads non démon en cours d'exécution; sinon, le thread de fin force un System.exit()appel, qui quitte la JVM (tuant les threads de démon en cours d'exécution). Ce n'est pas un problème lié au GC; d'une certaine manière, les threads sont alloués manuellement. Cependant, c'est ainsi que la JVM peut tolérer les threads semi-escrocs. Ceci est généralement utilisé pour les Timerinstances.

Thomas Pornin
la source
19

La JVM a une référence à tous les threads en cours d'exécution.

Aucun thread (ou les choses auxquelles il fait référence) ne sera récupéré pendant qu'il est encore en cours d'exécution.

Thilo
la source
13

Le thread n'est pas garbage collection car il existe des références aux threads que vous ne pouvez pas voir. Par exemple, il existe des références dans le système d'exécution.

Lorsque le thread est créé, il est ajouté au groupe de threads actuel. Vous pouvez obtenir une liste de threads dans le groupe de threads actuel, c'est donc une autre façon d'obtenir une référence à celui-ci.

vy32
la source