Java线程和垃圾收集器

cib*_*en1 20 java multithreading garbage-collection

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

考虑以下课程:

class Foo implements Runnable {

  public Foo () {
       Thread th = new Thread (this);
       th.start();
  }

  public run() {
    ... // long task
  }

}
Run Code Online (Sandbox Code Playgroud)

如果我们创建几个实例Foo

new Foo();
new Foo();
new Foo();
new Foo();
Run Code Online (Sandbox Code Playgroud)

(请注意,我们没有指向它们的指针).

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

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

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

谢谢

Luc*_*uca 19

  1. 可以不取消分配活动线程引用的任何对象.
  2. 是的,在`run()'中的线程结束后,GC将删除实例.
  3. 没问题.


Tom*_*icz 11

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

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

Customer c = new Customer();
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

你的代码很好.然而:

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

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

  • 从线程安全的角度来看,在构造函数中启动新线程也是一个糟糕的想法.在这个简单的情况下,可能存在问题,但如果您将类子类化并在子类构造函数中初始化了一些最终字段,则最终可能会在访问之前不对其进行初始化. (4认同)

Aar*_*lla 7

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

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

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

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

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