Java:为什么不收集垃圾?

关于GCing理论的快速问题。 我有以下方法。 它运行,并退出该方法。 为什么即使在GC运行后,计时器仍然存在并保持“TICK”? 我不相信在这种方法存在之后仍然存在对定时器或时间任务的引用,所以我希望定时器被GCed并导致exception。 请帮我理解这个概念。

谢谢,jbu

private void startTimer() { Timer timer= new Timer(); TimerTask timerTask= new TimerTask() { @Override public void run() { System.out.println("TICK"); } }; timer.scheduleAtFixedRate(timerTask, 0, 500); } 

Timer对象实际上调度要在后台线程中执行的任务,以便后台线程维护对Timer(和TimerTask)的引用,这可以防止两者被垃圾回收。

以下是文档的适当引用:

在对Timer对象的最后一次实时引用消失并且所有未完成的任务都已完成执行之后,计时器的任务执行线程正常终止(并且变为垃圾回收)。 但是,这可能需要很长时间才能发生。 默认情况下,任务执行线程不作为守护程序线程运行,因此它能够阻止应用程序终止。 如果调用者想要快速终止计时器的任务执行线程,则调用者应该调用计时器的cancel方法。

因此,“所有未完成的任务已完成执行”的条件不满足,并且线程永远不会终止,因此Timer / TimerTask永远不会GC。

因为Timer有一个后台线程继续运行 :

对应于每个Timer对象的是一个后台线程,用于按顺序执行所有计时器的任务。 计时器任务应该快速完成。 如果计时器任务需要花费过多的时间来完成,它会“占用”计时器的任务执行线程。 反过来,这可以延迟后续任务的执行,后续任务可以在紧急任务最终完成时(以及如果)快速连续地“聚集”并执行。

由于它是后台线程,因此一直持续到JVM退出或停止。

更新:关于此更多一点。 “后台线程”与守护程序线程相同 – 通过与BSD守护程序进程类比命名。 如果你在Thread上看到javadocs,你会发现:

将此线程标记为守护程序线程或用户线程。 当运行的唯一线程都是守护程序线程时,Java虚拟机将退出。

当主终止时,所有用户线程都会停止,只留下守护程序线程。 然后JVM关闭。 好时光 – 如果很短 – 调用Thread.currentThread().setDaemon(true); 来自主要。

更新:Ack。 我几乎是对的。 您必须在构建时将计时器作为守护程序。 (这是改变了,还是只是脑function衰竭?)

无论如何,这里是示例代码:

 import java.util.*; class Chatter extends TimerTask { public void run(){ System.err.println("Timer run."); } } public class TryThread { public static void main(String[] argv){ // If argument is true, only runs a few times. Timer t = new Timer(false); t.schedule(new Chatter(), 1L, 1L); return ; } } 

计时器没有被垃圾收集,因为它仍然在运行 – 其他一些对象(例如线程调度程序)仍然有一个对它的引用,这可能是在scheduleAtFixedRate()创建的。

你怎么知道GC跑了? 垃圾收集一般不是确定性的,它绝对不是由方法范围触发的。 它不像C ++,你可以放弃函数的范围和析构函数。 如果GC感觉如此,那么它将会收集内存。