Java:为什么这不会被垃圾收集?

时间:2009-04-20 17:51:08

标签: java garbage-collection

关于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);
    }

4 个答案:

答案 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感觉如此,它将会收集内存。