Java线程是否被垃圾回收了?

93

这个问题在某个网站上发布过。我在那里没有找到正确的答案,所以我在这里再次发布它。

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

我的问题不是关于停止线程的。让我重新阐述一下我的问题。 第A行(见上面的代码)开始了一个新线程;而第B行使线程引用为空。因此,JVM现在有一个线程对象(处于运行状态),但没有对该对象的引用存在(因为第B行中t=null)。 所以我的问题是,为什么这个线程(在主线程中不再有引用)会一直运行,直到主线程结束。根据我的理解,线程对象应该在第B行后被垃圾回收。我尝试运行这段代码5分钟以上,并请求Java运行时运行GC,但线程就是不停止。
4个回答

138

一个正在运行的线程被认为是所谓的垃圾回收根之一,它是阻止东西被垃圾回收的因素之一。当垃圾收集器确定您的对象是否 'reachable' 时,它总是使用垃圾收集器根集作为参考点。

考虑一下,为什么您的主线程不会被垃圾回收,也没有任何人引用它。


22
这个回答当前的状态引发了一个问题,即线程是否可以被垃圾回收(在它们终止之后)。由于这个问题标记为 这个问题 的重复,应该提到在线程终止后,它们不再被标记为“垃圾收集根”,因此它们变得可达并可以被垃圾回收。 - bluenote10
14
最后一句话很深刻:“为什么你的主线程没有被垃圾回收...”。 - Determinant
作为后续,一个已释放的线程(已加入的线程)不再被视为根吗?我几乎可以肯定答案是肯定的,但在分析器下我看到了一些奇怪的东西,这让我感到疑惑... - Groostav
1
我读的答案越多,就越感到困惑,但是为什么主线程没有被垃圾回收是值得思考的问题。但是回到核心问题,如果子线程创建了一些对象,由于父线程仍在运行并持有子线程的引用,即使它们已经“运行”完毕,这些对象也不会被垃圾回收。 - Rahul Kumar
@Groostav,已经加入的线程不在运行,因此它不是垃圾回收根。但是,当父线程调用child.join()时,它引用了child。如果该引用(以及任何其他引用)未被丢弃,则无法对子线程进行垃圾回收。 - Blaisorblade
显示剩余2条评论

25

正如所解释的那样,正在运行的线程根据定义是不受GC影响的。GC从扫描“根”开始工作,“根”被认为是始终可达的;“根”包括全局变量(Java中的“静态字段”)和所有正在运行的线程的堆栈(可以想象正在运行的线程的堆栈引用相应的Thread实例)。

然而,您可以将线程设置为“守护”线程(见Thread.setDaemon(boolean))。守护线程与非守护线程一样不会被垃圾回收,但当所有正在运行的线程都是守护线程时,JVM会退出。一种想象的方法是,每个线程在终止时都会检查是否还有一些非守护运行的线程;如果没有,则终止线程强制调用System.exit(),这将退出JVM(关闭正在运行的守护线程)。这不是与GC相关的问题;在某种程度上,线程是手动分配的。但这是JVM如何容忍半流氓线程的方式。这通常用于Timer实例。


21

JVM持有所有运行线程的引用。

只要线程仍在运行,它所引用的任何内容都不会被垃圾回收。


14

线程不会被垃圾回收,因为有你看不到的线程引用。例如,运行时系统中的引用。

当线程创建时,它被添加到当前线程组中。你可以获取当前线程组中线程的列表,因此这是另一种获取对其引用的方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接