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

jbu*_*jbu 24 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);
    }
Run Code Online (Sandbox Code Playgroud)

Ric*_*and 37

定时器对象实际上调度任务以在后台线程中被执行,从而使后台线程保持到定时器(和TimerTask的),这防止两者被垃圾收集的参考.

以下是文档的适当引用:

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

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


Cha*_*tin 12

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

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

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

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

将此线程标记为守护程序线程或用户线程.当运行的唯一线程都是守护程序线程时,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 ;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 只是为了强调查理的观点,实时线程是垃圾收集器的"根"对象.它们在死亡之前不是垃圾,也不能收集.从活线程中强烈引用的任何东西都不是垃圾.加载的类以类似的方式工作. (2认同)

Nic*_*eys 0

你怎么知道GC运行了?一般来说,垃圾收集不是一个确定性的事情,它绝对不是由方法作用域触发的。它不像 C++,你保留函数的作用域并触发析构函数。如果 GC 愿意的话,它会开始收集内存。