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

发布于 2024-12-09 09:41:45 字数 817 浏览 0 评论 0原文

在此代码示例中,使用了 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 保持活动状态。甚至调用 System.gc() 似乎也不起作用。为什么即使 main() 终止后 executorService 也没有被收集?

注意:我知道我应该自己调用 shutdown() 并且我总是在测试之外调用。我很好奇为什么最终确定在这里不起作用。

In this code example, the ExecutorService is used one and allowed to go out of scope.

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

Once executorService is out of scope, it should get collected and finalized. The finalize() method in ThreadPoolExecutor calls shutdown().

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

Once shutdown() is called, the pool threads should terminate and the JVM should be allowed to exit. However the executorSerivce is never getting collected and thus the JVM stays alive. Even calls to System.gc() don't seem to work. Why isn't executorService getting collected even after main() terminates?

Note: I know I should call shutdown() myself and I always do outside of testing. I'm curious why finalization isn't working as a back-up here.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

撩发小公举 2024-12-16 09:41:45

这实际上与 GC 的不确定性没有任何关系,尽管它没有帮助! (这是您示例中的原因之一,但即使我们“修复”它以消耗内存并强制进行集合,它仍然不会最终确定)

执行器创建的工作线程是内部类,它们具有对执行者本身。 (他们需要它才能看到队列、运行状态等!)正在运行的线程不会被垃圾收集,因此池中的每个线程都有该引用,它们将使执行器保持活动状态,直到所有线程都死掉。如果您不手动执行某些操作来停止线程,它们将永远保持运行,并且您的 JVM 将永远不会关闭。

This doesn't really have anything to do with GC being non-deterministic, although it doesn't help! (That is one cause in your example, but even if we 'fixed' it to eat up memory and force a collection, it still wouldn't finalize)

The Worker threads that the executor creates are inner classes that have a reference back to the executor itself. (They need it to be able to see the queue, runstate, etc!) Running threads are not garbage collected, so with each Thread in the pool having that reference, they will keep the executor alive until all threads are dead. If you don't manually do something to stop the threads, they will keep running forever and your JVM will never shut down.

于我来说 2024-12-16 09:41:45

阿菲是正确的;线程池的线程将防止它被垃圾收集。当你调用 Executors.newFixedThreadPool(3) 时,你会得到一个像这样构造的 ThreadPoolExecutor:

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

如果你阅读 ThreadPoolExecutor 的 JavaDoc,它会说:

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

如果您希望线程池像您期望的那样完成,您应该执行其中一项操作。

Affe is correct; the thread pool's threads will keep it from being garbage collected. When you call Executors.newFixedThreadPool(3) you get a ThreadPoolExecutor constructed like so:

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

And if you read the JavaDoc for ThreadPoolExecutor it says:

A pool that is no longer referenced in a program AND has no remaining
threads will be shutdown automatically. If you would like to ensure
that unreferenced pools are reclaimed even if users forget to call
shutdown(), then you must arrange that unused threads eventually die,
by setting appropriate keep-alive times, using a lower bound of zero
core threads and/or setting allowCoreThreadTimeOut(boolean).

If you want your thread pool to finalize like you're expecting, you should do one of those things.

苍暮颜 2024-12-16 09:41:45

如果您希望在 Executor 服务超出范围时最终确定线程,您应该像 mjt 建议的那样避免使用

    ExecutorService executorService = Executors.newFixedThreadPool(3);`

和 use 例如:

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

If you want the threads to be finalized when the Executor service is out of scope you should avoid, as mjt suggested, the use of

    ExecutorService executorService = Executors.newFixedThreadPool(3);`

and use for example:

ExecutorService executorService = new ThreadPoolExecutor(0, 3, 10, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
睡美人的小仙女 2024-12-16 09:41:45

终结器太不可预测了。依赖它们通常是不好的做法。
您可以在 Joshua Bloch 的“Effective java”(第 1.7 项)

Finalizers are too unpredictable. Depending on them is usually bad practice.
You can read more about it in "Effective java" by Joshua Bloch (item 1.7)

撩起发的微风 2024-12-16 09:41:45

因为垃圾收集是“不确定的”,即您无法预测它何时发生,因此您无法准确预测 Finalize 方法何时运行。您只能使对象符合GC并建议使用System.gc()进行GC,而没有任何保证。

更糟糕的线程是由 JVM 处理的操作系统特定的并且很难预测......

Because garbage collection is “non deterministic” ie you cannot predict when it will happen, you thus cannot predict exactly when the finalize method will run. You can only make Objects eligible for GC and suggest gc with System.gc() without any guarantee.

Even worse threads are OS specific handled by the JVM and are hardly predictable...

子栖 2024-12-16 09:41:45

一旦 executorService 超出范围,它应该被收集并最终确定。

事实并非如此——一旦超出范围,它就可以被收集并最终确定。 VM 规范 关于对象何时最终确定,或者甚至如果它们已最终确定:

Java 编程语言没有指定多久将调用终结器,只是说它将在重用对象的存储之前发生。

Once executorService is out of scope, it should get collected and finalized.

Not really - once it is out of scope, it could get collected and finalized. There are no guarantees made in the VM spec about when objects are finalized, or even if they are finalized:

The Java programming language does not specify how soon a finalizer will be invoked, except to say that it will happen before the storage for the object is reused.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文