23

可能重复:
Java Thread Garbage 是否已收集

考虑以下类:

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();

(请注意,我们不保留指向它们的指针)。

  1. 垃圾收集器是否可以在线程结束之前删除这些实例run()?(换句话说:是否有任何对Foo对象的引用?)

  2. 另一方面,这些实例会在 `run()' 中的线程结束后被 GC 删除,还是我们在浪费内存(“内存泄漏”)?

  3. 如果 1. 或 2. 有问题,那么正确的方法是什么?

谢谢

4

5 回答 5

22
  1. 任何被活动线程引用的对象都不能被释放。
  2. 是的,实例将在 `run()' 中的线程结束后被 GC 删除。
  3. 没问题。
于 2012-04-30T08:08:24.817 回答
12
  1. 垃圾收集器是否可以在 run() 中的线程结束之前删除这些实例?(换句话说:是否有任何对 Foo 对象的引用?)

不会。当构造函数运行时,GC 不会收集对象。否则即使是最简单的:

Customer c = new Customer();

的构造函数Customer运行时可能会失败。另一方面,当您启动一个新线程时,该线程对象将成为一个新的GC 根,因此该对象引用的所有内容都不是垃圾回收的对象。

  1. 另一方面,这些实例会在 `run()' 中的线程结束后被 GC 删除,还是我们在浪费内存(“内存泄漏”)?

一旦线程完成,它就不再是 GC 根。如果没有其他代码指向该线程对象,它将被垃圾回收。

  1. 如果 1. 或 2. 有问题,那么正确的方法是什么?

你的代码很好。然而:

  • 从单元测试的角度来看,在构造函数中启动一个新线程是一个坏主意

  • 如果您想稍后中断这些线程,则保留对所有正在运行的线程的引用可能是有益的。

于 2012-04-30T08:10:31.947 回答
8

在不指定线程组的情况下启动新线程会将其添加到默认组

如果 group 为 null 并且有安全管理器,则该组由安全管理器的 getThreadGroup 方法确定。如果 group 为 null 并且没有安全管理器,或者安全管理器的 getThreadGroup 方法返回 null,则将 group 设置为与正在创建新线程的线程相同的 ThreadGroup。

只要该组还活着,该组就会保留对该线程的引用,因此在此期间不能对其进行 GC。

当线程终止时(=run()无论出于何种原因返回时),该线程将从线程组中删除。这发生在exit()从本机代码调用的私有方法中。这是对线程的最后一个引用丢失并且符合 GC 条件的时间点。

请注意,代码表明ThreadGroup可以,null但事实并非如此。各种空值检查只是为了在出现问题的罕见情况下避免 NPE。在Thread.init()中,如果 Java 无法确定线程组,您将获得 NPE。

于 2012-04-30T08:14:12.553 回答
3
  1. Foo对象由线程引用。线程在其运行期间一直被引用。因此它不会被垃圾收集。
  2. 没有内存泄漏。线程将结束,并将Foo在此过程中被垃圾收集和对象。
  3. 它应该可以正常工作。
于 2012-04-30T08:08:37.277 回答
0

假设您在 run 方法中创建对象,当 run 方法退出时,对象将超出范围,然后可用于垃圾回收。运行只是另一种方法。是否使用线程不会以任何方式改变这里的垃圾收集行为。您只需要关心对象何时超出范围,这通常与块范围(方法块、while 循环、if 块等)相关联。

因此,由于您没有保留对对象的任何引用,因此您可能希望将创建对象的逻辑提取到其自己的短期方法中。这样,创建的对象就不需要超出该方法的范围。

于 2012-04-30T08:47:26.933 回答