为什么这个线程池没有被垃圾回收?
在此代码示例中,使用了 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这实际上与 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.
阿菲是正确的;线程池的线程将防止它被垃圾收集。当你调用 Executors.newFixedThreadPool(3) 时,你会得到一个像这样构造的 ThreadPoolExecutor:
如果你阅读 ThreadPoolExecutor 的 JavaDoc,它会说:
如果您希望线程池像您期望的那样完成,您应该执行其中一项操作。
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:
And if you read the JavaDoc for ThreadPoolExecutor it says:
If you want your thread pool to finalize like you're expecting, you should do one of those things.
如果您希望在 Executor 服务超出范围时最终确定线程,您应该像 mjt 建议的那样避免使用
和 use 例如:
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
and use for example:
终结器太不可预测了。依赖它们通常是不好的做法。
您可以在 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)
因为垃圾收集是“不确定的”,即您无法预测它何时发生,因此您无法准确预测 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...
事实并非如此——一旦超出范围,它就可以被收集并最终确定。 VM 规范 关于对象何时最终确定,或者甚至如果它们已最终确定:
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: