考虑以下课程:
class Foo implements Runnable {
public Foo () {
Thread th = new Thread (this);
th.start();
}
public run() {
... // long task
}
}
如果我们通过
创建多个Foo
实例
new Foo();
new Foo();
new Foo();
new Foo();
(请注意,我们不会保留指向它们的指针)。
在之前,垃圾收集器是否可以删除这些实例
run()
中的帖子结束了吗? (换句话说:有没有参考
到Foo
个对象?)
而另一方面,GC会删除这些实例 在`run()'中的线程结束之后,还是我们在浪费内存(“内存泄漏”)?
如果1.或2.是问题,那么正确的做法是什么?
由于
答案 0 :(得分:18)
答案 1 :(得分:11)
- 在run()中的线程结束之前,垃圾收集器是否可以删除这些实例? (换句话说:是否有对Foo对象的引用?)
醇>
没有。虽然构造函数正在运行GC但不会收集对象。否则即使是最简单的:
Customer c = new Customer();
Customer
的构造函数正在运行时,可能会失败。另一方面,当您启动一个新线程时,该线程对象将成为一个新的 GC根,因此该对象引用的所有内容都不是垃圾回收的主题。
- 而另一方面,在`run()'中的线程结束后,GC会删除那些实例,还是我们在浪费内存(“内存泄漏”)?
醇>
线程完成后,它不再是GC根目录。如果没有其他代码指向该线程对象,则将对其进行垃圾回收。
- 如果1.或2.是问题,那么正确的方法是什么?
醇>
你的代码很好。但是:
在构造函数中开始一个新线程是个坏主意
保持对所有正在运行的线程的引用可能是有益的,例如,你想稍后打断这些线程。
答案 2 :(得分:7)
在未指定线程组的情况下启动新线程add it to the default group:
如果group为null且有安全管理器,则该组由安全管理器的getThreadGroup方法确定。如果group为null且没有安全管理器,或者安全管理器的getThreadGroup方法返回null,则该组将设置为与创建新线程的线程相同的ThreadGroup。
只要该线程处于活动状态,该组就会保留对该线程的引用,因此在此期间无法进行GC。
当线程终止时(= run()
因任何原因返回时),线程将从线程组中删除。这发生在从本机代码调用的私有方法exit()
中。这是对线程的最后一次引用丢失并且符合GC条件的时间点。
请注意,代码表明ThreadGroup
可以是null
,但事实并非如此。各种空检仅仅是为了避免NPE在极少数情况下出现问题。在Thread.init()
中,如果Java无法确定线程组,您将获得NPE。
答案 3 :(得分:2)
Foo
对象。线程在其运行期间始终被引用。因此它不会被垃圾收集。Foo
对象。答案 4 :(得分:0)
假设您正在run方法中创建对象,当run方法退出时,对象将超出范围,然后可用于垃圾回收。运行只是另一种方法。是否使用线程不会以任何方式更改此处的垃圾回收行为。所有你需要关心的是当对象超出范围时,这通常与块范围有关(方法块,while循环,if块等等。)。
因此,由于您没有开始对该对象的任何引用,您可能希望将创建该对象的逻辑提取到其自己的短期方法中。这样,创建的对象不需要超出该方法的范围。