Java线程和垃圾收集器

可能重复:
是否收集了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.是问题,那么正确的方法是什么?

谢谢

  1. 活动线程引用的任何对象都不能被解除分配。
  2. 是的,在`run()’中的线程结束后,GC将删除实例。
  3. 没问题。
  1. 在run()中的线程结束之前,垃圾收集器是否可以删除这些实例? (换句话说:是否有对Foo对象的引用?)

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

 Customer c = new Customer(); 

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

  1. 而且,另一方面,在`run()’中的线程结束后,或者我们是否在浪费内存(“内存泄漏”)后,GC会删除这些实例吗?

线程完成后,它不再是GC根目录。 如果没有其他代码指向该线程对象,则将对其进行垃圾回收。

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

你的代码很好。 然而:

  • 从unit testing的角度来看,在构造函数中开始一个新线程是一个糟糕的主意

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

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

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

只要它处于活动状态,该组将保留对该线程的引用,因此在此期间它不能进行GC。

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

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

  1. 线程引用Foo对象。 线程在其运行期间始终被引用。 因此它不会被垃圾收集。
  2. 没有内存泄漏。 线程将结束并将在此过程中进行垃圾收集和Foo对象。
  3. 它应该工作正常。

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

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