【问题标题】:Java: Why does this not get garbage collected?Java:为什么不收集垃圾?
【发布时间】:2009-04-20 17:51:08
【问题描述】:

关于 GCing 理论的快速问题。我有以下方法。它运行并退出该方法。为什么GC运行后,计时器仍然存在并保持“滴答”?我不相信在这个方法存在之后仍然有对 timer 或 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);
    }

【问题讨论】:

  • 如果它真的消失了不是很可怕吗?这将使几乎所有的多任务处理变得更加困难。

标签: java garbage-collection


【解决方案1】:

Timer 对象实际上将任务安排在后台线程中执行,以便后台线程维护对 Timer(和 TimerTask)的引用,从而防止两者都被垃圾回收。

这是来自文档的适当引用:

在最后一次实时引用之后 计时器对象消失,所有 未完成的任务已完成 execution,定时器的任务执行 线程优雅地终止(并且 成为垃圾 收藏)。但是,这可能需要 任意长的时间发生。默认情况下, 任务执行线程不运行 作为一个守护线程,所以它有能力 阻止申请 终止。如果来电者想 终止计时器的任务执行 线程快速,调用者应该 调用定时器的取消方法。

所以“所有未完成的任务都执行完毕”的条件不满足,线程永远不会终止,所以Timer/TimerTask永远不会被GC。

【讨论】:

  • 没错。来自 Timer 的 JavaDocs:“对应于每个 Timer 对象的是一个后台线程,用于按顺序执行所有计时器的任务。”
  • 但是,如果我取消 Timer,Timer/TimerTask 最终会被 GC´ed 吗?
【解决方案2】:

因为 Timer 有一个background thread that continues running

对应每个Timer对象是 一个后台线程 用于执行所有定时器的 任务,顺序。定时器任务 应该很快完成。如果一个计时器 任务需要过多的时间才能完成, 它“占用”了计时器的任务执行 线。这反过来又会延迟 执行后续任务,其中 可能会“聚集”并快速执行 当(如果)违规时的继承 任务终于完成了。

因为是后台线程,所以一直持续到JVM退出或者停止。

更新:关于此的更多信息。 “后台线程”与守护线程相同——命名方式与 BSD 守护进程类似。如果您在 Thread 上看到 javadocs,您会发现:

将此线程标记为守护进程 线程或用户线程。爪哇 虚拟机退出时唯一 运行的线程都是守护进程 线程。

当你的主线程终止时,所有用户线程都停止,只留下守护线程。 JVM 然后关闭。为了一段美好的时光——如果时间很短——请从 main 中致电 Thread.currentThread().setDaemon(true);

更新:确认。我说的几乎是对的。您必须在构建时使计时器成为守护进程。 (这是改变了,还是我只是脑残了?)

不管怎样,下面是示例代码:

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 ;
    }
}

【讨论】:

  • 只是为了强调查理的观点,活动线程是垃圾收集器的“根”对象。他们死之前不是垃圾,也不能被收集。任何来自活动线程的强引用也不是垃圾。加载的类以类似的方式工作。
  • 等等,这个定时器在调用它的程序完成后还能继续执行吗?
  • 不是,因为当启动时调用的public static void main终止时,JVM整体关闭,也就意味着停止和杀死后台线程。但它并没有真正得到 GC,因为当进程终止时,所有内存都会被释放。
  • @Charlie Martin:只有在没有其他非守护线程的情况下才会这样。例如,Swing 应用程序通常有一个 main(),它只调用 GUI 并死掉。正如 Rick Copeland 的回答中的 Javadoc 引用所解释的那样,计时器确实可以阻止 JVM 退出。
  • 两个不同的程序可以在同一个JVM上运行吗?还是一个程序=一个JVM?
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

    • 我已经通过netbeans的调试模式显式调用了GC。
    猜你喜欢
    • 1970-01-01
    • 2010-12-19
    • 2014-08-08
    • 2010-11-10
    • 1970-01-01
    • 2022-01-12
    • 2013-04-25
    • 1970-01-01
    • 2010-12-13
    相关资源
    最近更新 更多