是否收集Java线程垃圾


85

该问题已发布在某个网站上。我在这里找不到正确的答案,因此我将其再次发布在这里。

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 Runtime运行GC,但是线程不会停止。

希望这次代码和问题都清楚。

Answers:


123

正在运行的线程被认为是所谓的垃圾回收根,并且是防止东西被垃圾回收的其中一种。当垃圾收集器确定您的对象是否“可达”时,它总是使用垃圾收集器根集作为参考点来这样做。

考虑一下,为什么您的主线程没有被垃圾回收,也没有人引用那个线程。


16
从目前的情况来看,这个答案提出了一个问题,即是否可以在线程终止后对它们进行GC处理。由于这个问题被标记为重复这一块,应该指出的是,线程将不再被标记为“垃圾收集根”,他们结束之后,因此,他们成为可达的GC。
bluenote10 2014年

13
最后一句话很刺眼:“为什么您的主线程没有被垃圾...”。
行列式

因此,作为后续措施,已处置线程(已加入的线程)将不再被视为根吗?我几乎可以肯定,答案是肯定的,但是我在探查器下的应用程序中看到了奇怪的事情,这让我感到
困惑

1
我读的答案越多,我就越困惑,但是是的,为什么要注意主线程没有被垃圾收集呢?但是回到核心问题,如果子线程创建了一些对象,则由于父线程仍在运行,即使子线程已经用完(!! ??),它也不会被GC,因为它们保留了子线程的引用(!! ??)
Rahul Kumar

@Groostav已加入的线程未在运行,因此它不是垃圾回收根。但是当父线程调用时child.join(),它具有对的引用child。如果该引用(和任何其他引用)没有被丢弃,则子线程无法被GC。
Blaisorblade

23

如前所述,根据定义,运行中的线程不受GC的影响。GC通过扫描被认为始终可以到达的“根”来开始工作。根包括全局变量(Java语言中的“静态字段”)和所有正在运行的线程的堆栈(可以想象,正在运行的线程的堆栈引用了相应的Thread实例)。

但是,您可以将线程设为“守护程序”线程(请参阅参考资料Thread.setDaemon(boolean))。守护程序线程不会比非守护程序线程更多地进行垃圾回收,但是当所有正在运行的线程都是守护程序时,JVM将退出。可以想象的一种方法是,每个线程在终止时都会检查是否还有一些非守护进程正在运行的线程。如果不是,终止线程将强制执行一个System.exit()调用,该调用将退出JVM(杀死正在运行的守护程序线程)。这不是与GC有关的问题。在某种程度上,线程是手动分配的。但是,这就是JVM可以容忍半恶意线程的方式。这通常用于Timer实例。


19

JVM引用了所有正在运行的线程。

线程(或它所引用的对象)在仍在运行时不会被垃圾回收。


13

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

创建线程后,它将被添加到当前线程组。您可以获得当前线程组中的线程列表,这是获取对其的引用的另一种方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.