java 为什么这个线程池没有被垃圾回收?

fkaflof6  于 2023-04-28  发布在  Java
关注(0)|答案(6)|浏览(417)

在此代码示例中,ExecutorService被使用一次,并允许超出范围。

public static void main(String[] args)
{
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    executorService.submit(new Runnable()
    {
        public void run()
        {
            System.out.println("hello");
        }
    });
}

一旦executorService超出范围,它就应该被收集并最终确定。ThreadPoolExecutor中的finalize()方法调用shutdown()。

/**
 * Invokes {@code shutdown} when this executor is no longer
 * referenced and it has no threads.
 */
protected void finalize() {
    shutdown();
}

一旦调用了shutdown(),池线程就应该终止,JVM应该被允许退出。但是,executorSerivce永远不会被收集,因此JVM保持活动状态。甚至调用系统。gc()似乎不起作用。为什么在main()终止后executorService也没有被收集?
注意:我知道我应该自己调用shutdown(),而且我总是在测试之外这样做。我很好奇为什么最后定稿在这里不起作用。

9cbw7uwe

9cbw7uwe1#

这与GC的非确定性没有任何关系,尽管它没有帮助!(这是你的例子中的一个原因,但是即使我们“修复”它来消耗内存并强制收集,它仍然不会最终完成)
执行器创建的Worker线程是内部类,它们具有对执行器本身的引用。(他们需要它才能看到队列、运行状态等!)运行的线程不会被垃圾回收,因此池中的每个线程都有该引用,它们将保持执行器活动,直到所有线程都死了。如果您不手动停止线程,它们将永远运行,JVM将永远不会关闭。

2ic8powd

2ic8powd2#

Affe是正确的;线程池的线程将防止它被垃圾收集。当你调用ExecutorsnewFixedThreadPool(3)你得到一个ThreadPoolExecutor,如下所示:

ThreadPoolExecutor(3, 3, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

如果你读了ThreadPoolExecutor的JavaDoc,它说:
程序中不再引用且没有剩余线程的池将自动关闭。如果您希望确保即使用户忘记调用shutdown()也能回收未引用的池,则**您必须通过设置适当的keep-alive时间、使用零核心线程下限和/或设置allowCoreThreadTimeOut(boolean)来安排未使用的线程最终死亡。
如果您希望线程池像您期望的那样完成,您应该做这些事情之一。

km0tfn4u

km0tfn4u3#

如果您希望在Executor服务超出范围时完成线程,则应避免使用mjt建议的

ExecutorService executorService = Executors.newFixedThreadPool(3);`

并使用例如:

ExecutorService executorService = new ThreadPoolExecutor(0, 3, 10, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
2izufjch

2izufjch4#

终结器太不可预测了。依赖它们通常是不好的做法。您可以在约书亚Bloch的“"Effective java”(第1项)中阅读更多信息。(七)

gblwokeq

gblwokeq5#

因为垃圾收集是“非确定性的”,即你不能预测它何时发生,因此你不能准确预测finalize方法何时运行。您只能使对象符合GC条件,并向系统建议GC。gc()没有任何保证。
甚至更糟糕的线程是由JVM处理的特定于OS的线程,几乎是不可预测的。..

ymzxtsji

ymzxtsji6#

一旦executorService超出范围,它就应该被收集并最终确定。
不是真的--一旦它超出了范围,它就可以被收集和最终确定。VM规范中没有保证对象何时被终结,甚至没有保证 * 如果 * 它们被终结:
Java编程语言没有指定多久调用终结器,只是说它将在对象的存储被重用之前发生。

相关问题