Java 线程垃圾回收

6

当线程中的run()方法执行完毕或调用该方法的函数执行完毕时,线程是否被垃圾回收?

我没有任何问题,但我想避免内存泄漏等情况。

对于喜欢代码的人:

public void SomeClass {
    public SomeClass() {

    }

    public void someMethod() {
        Object data = veryBigObject;

        while(true) {
            MyThread thread = new MyThread(veryBigObject);
            thread.run();

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) { /* do nothing */ }
        }
    }

    public static void main(String[] args) {
        SomeClass myclass = SomeClass();
        myclass.someMethod();
    }
}


public class MyThread extends Thread {
    private Object data;

    public Thread(Object data) {
        this.data = data;
    }

    public void run() {
        // do stuff with data
    }
}

在我所知道的所有编程语言中,垃圾(如果该语言有)都是在方法结束时收集的。我认为Java也是如此。这意味着在someMethod()中创建的线程在someMethod()结束之前理论上不会被收集。如果我们假设while循环运行了很长时间,应用程序将耗尽内存并崩溃。

我的问题是:这是真的吗?如果是,该怎么避免?


1
https://dev59.com/uHE95IYBdhLWcg3wKq2q#2423293 - Mob
2
在我所知道的所有编程语言中,(如果该语言有)垃圾回收都是在方法结束时进行的。您说的只适用于一种语言!您混淆了自动引用计数和C++类似的堆栈分配对象的析构函数。Java不会在堆栈上分配任何对象(出于设计考虑,但通过EA [逃逸分析]或ED [逃逸检测]技术在技术上是可能的),因此该语句在最好的情况下是不正确的(在最坏的情况下非常错误)。 - bestsss
5个回答

7

首先,在Java中,GC是非确定性的。当对象不再被引用时,它适合在下一次GC运行时进行回收,但这并不是必须的。甚至调用System.gc()仅仅是对GC系统的建议。

其次,重要的是引用。在循环的每个实例中,您都会覆盖线程对象的引用。如果线程已经完成,则无需退出方法来进行回收(当然,当线程正在运行时,即使您的代码没有对其引用,它们也不会被回收)。离开创建对象的方法的问题并不相关。


所以因为我正在覆盖引用,当线程运行结束时,线程是不可达的,因此会被垃圾回收,对吗? - Tim S.
当线程运行完成时,除了线程完成运行之外,没有任何特殊情况发生。垃圾回收器可以在任何时间运行。 - Peter Lawrey

4

当需要时(例如,尝试分配内存且eden空间已满)GC将运行

这可能发生在任何分配内存的代码行中(在任何线程中)。这意味着即使您不在一个线程中分配内存,仍可以发生GC,因为在另一个线程中分配了内存。

您还可以显式触发GC或使用像visualvm这样的JMX工具。

简而言之:GC可能随时发生。


1
一个真正的并发GC可以在任何时候运行,甚至可以一直运行(尽管后者需要读屏障)。 - bestsss

1

每个MyThread实例在另一个循环迭代开始后都会被标记为GC收集。如果您想将MyThread作为线程运行,则需要使用start()方法!

如果您使用start()方法,那么MyThread将在另一个循环迭代开始后和run()方法结束时被标记为GC收集。

但是不能保证它何时被GC收集。这是GC特定的事情。


0
在您上面发布的代码中,实际上没有任何线程并行执行。要生成新线程,您需要调用 .start() 方法,该方法内部调用线程的 .run() 方法。现在,一切都是按顺序运行的。因此,您的 thread.run() 将完成其执行,当前线程将休眠,而您的 while 将继续其循环并一遍又一遍地执行。
垃圾回收器可以在任何时候启动,并且正如其他人所述,只有已完成执行的运行线程(使用 start() 启动的线程)才会被 GC 回收。

0

假设数据已经完成并且不再使用后,它将在线程上调用,但不知道何时会调用,因为虚拟机会定期自动调用垃圾回收器或发生某些事件。

因此,当线程终止并且数据变得无用时,您无法确定数据是否已被自动收集。

您还可以使用以下方式显式调用:System.gc()

如果您的线程已终止,并且它指向的数据不再使用,则在显式调用之后,它已被回收。


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