为什么这个线程池没有收集垃圾?

Cra*_*lin 31 java concurrency garbage-collection

在此代码示例中,ExecutorService使用了一个并允许超出范围.

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

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

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

调用shutdown()后,池线程应终止,并允许JVM退出.但是,执行程序服务永远不会被收集,因此JVM保持活动状态.甚至对System.gc()的调用似乎也不起作用.为什么即使在main()终止后,executorService也没有被收集?

注意:我知道我应该自己调用shutdown(),而且我总是在测试之外做.我很好奇为什么最终确定不作为备份在这里工作.

Aff*_*ffe 50

这与GC不确定无关,但它没有帮助!(这是你的例子中的一个原因,但即使我们"修复它"以消耗内存并强制收集,它仍然无法完成)

执行程序创建的Worker线程是内部类,它们具有对执行程序本身的引用.(他们需要它能够看到队列,runstate等等!)运行线程不是垃圾收集的,所以当池中的每个线程都有该引用时,它们将使执行器保持活动状态直到所有线程都死亡.如果您不手动执行某些操作来停止线程,它们将永远保持运行,您的JVM将永远不会关闭.


mjt*_*mjt 20

情感是正确的; 线程池的线程将阻止它被垃圾收集.当您调用Executors.newFixedThreadPool(3)时,您将获得如此构造的ThreadPoolExecutor:

ThreadPoolExecutor(3, 3, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
Run Code Online (Sandbox Code Playgroud)

如果您阅读了ThreadDoolExecutor的JavaDoc,它会说:

程序中不再引用且没有剩余线程的池将自动关闭.如果您希望确保即使用户忘记调用shutdown()也会回收未引用的池,那么您必须 通过设置适当的保持活动时间,使用零核心线程的下限和/或来安排未使用的线程最终死亡设置allowCoreThreadTimeOut(boolean).

如果你想让你的线程池像你期望的那样完成,你应该做其中的一件事.