关于GCing理论的快速提问。我有以下方法。它运行,并退出该方法。为什么即使在GC运行后,计时器仍然存在并保持“TICK”?我不相信在这个方法存在之后仍然有对定时器或timertask的引用,所以我希望定时器被GCed并导致异常。请帮我理解这个概念。
谢谢, 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);
}
答案 0 :(得分:34)
Timer对象实际上调度要在后台线程中执行的任务,以便后台线程维护对Timer(和TimerTask)的引用,这样就可以防止这两者被垃圾回收。
以下是文档的适当引用:
在最后一次实时参考之后 定时器对象消失了 已完成的任务已完成 执行,计时器的任务执行 线程优雅地终止(和 变得垃圾 采集)。但是,这可以采取 任意长时间发生。默认情况下, 任务执行线程不运行 作为守护程序线程,所以它是有能力的 保持申请 终止。如果来电者想要 终止计时器的任务执行 线程很快,调用者应该 调用计时器的取消方法。
因此不满足“所有未完成的任务已完成执行”的条件,并且线程永远不会终止,因此Timer / TimerTask永远不会GC。
答案 1 :(得分:12)
因为计时器有background thread that continues running:
对应每个Timer对象 一个单一的后台线程 用于执行所有计时器 任务,顺序。计时器任务 应该快点完成。如果是计时器 任务需要很长时间才能完成, 它“占用”计时器的任务执行 线。反过来,这可以延迟 执行后续任务,其中 可以“束缚”并迅速执行 当(以及如果)冒犯时继承 任务终于完成了。
由于它是后台线程,因此它会持续到JVM退出或停止为止。
更新:对此更多一点。 “后台线程”与守护程序线程相同 - 通过与BSD守护程序进程类比命名。如果您在Thread上看到javadoc,您会发现:
将此主题标记为守护程序 线程或用户线程。 Java 虚拟机仅在退出时退出 运行的线程都是守护进程 线程。
当主终止时,所有用户线程都会停止,只留下守护程序线程。然后JVM关闭。好好的时间 - 如果很短 - 请从主要电话Thread.currentThread().setDaemon(true);
。
更新:Ack。我有几乎正确。您必须在构建时将计时器作为守护程序。 (这是改变了,还是我只是脑功能衰竭?)
无论如何,这是示例代码:
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 ;
}
}
答案 2 :(得分:2)
计时器没有被垃圾收集,因为它仍然在运行 - 其他一些对象(例如线程调度程序)仍然有一个对它的引用,这可能是在scheduleAtFixedRate()
内创建的。
答案 3 :(得分:0)
你怎么知道GC跑了?垃圾收集一般不是确定性的,它绝对不是由方法范围触发的。它不像C ++,你可以放弃函数的范围和析构函数。如果GC感觉如此,它将会收集内存。