ChatGPT解决这个技术问题 Extra ChatGPT

Java 线程垃圾是否收集

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

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,但线程并没有停止。

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


k
kiranpradeep

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

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


就目前而言,这个答案提出了一个问题,即线程是否可以被 GC(在它们终止之后)。由于这个问题被标记为 this one 的重复,应该提到线程在终止后将不再被标记为“垃圾收集根”,因此它们可以被 GC 访问。
最后一句话很有穿透力:“为什么你的主线程不是垃圾……”。
那么作为后续,已处理的线程(已加入的线程)是否不再被视为根?我几乎可以肯定答案是肯定的,但是我在探查器下的应用程序上看到了奇怪的东西,这让我想知道......
我读的答案越多,我就越困惑,但是是的,为什么主线程没有收集垃圾是值得思考的。但是回到核心问题,如果子线程创建了一些对象,它就不会被 GC,因为父线程仍在运行并持有子线程的引用,即使它们已经“用完”(!!??)
@Groostav 已加入的线程未运行,因此它不是垃圾收集根。但是当父线程调用 child.join() 时,它引用了 child。如果该引用(和任何其他引用)未被丢弃,则子线程无法被 GC。
T
Thomas Pornin

如前所述,根据定义,正在运行的线程对 GC 是免疫的。 GC 通过扫描“根”开始其工作,这些“根”被认为始终可以访问;根包括全局变量(Java-talk 中的“静态字段”)和所有正在运行的线程的堆栈(可以想象一个正在运行的线程的堆栈引用了相应的 Thread 实例)。

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


T
Thilo

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

当线程仍在运行时,不会对线程(或它所指的东西)进行垃圾收集。


v
vy32

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

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