executorService.submit(Runnable) 返回的未来对象是否包含对 runnable 对象的任何引用?

2022-09-04 23:33:45

假设我们有以下代码:

List<Future<?>> runningTasks;
ExecutorService executor;
...
void executeTask(Runnable task){
    runningTasks.add(executor.submit(task));
}

我的问题是:

  1. 是否包含对对象的引用?runningTaskstask
  2. 它能维持多久?任务完成后,它是否仍保留它?
  3. 为了避免内存泄漏,我是否必须注意删除添加到列表中的未来?

答案 1

直到执行器或对象持有对它的引用才是实现细节。因此,如果任务使用大量内存,因此您必须担心内存使用情况,则应在任务完成之前显式清理任务。Future

如果你看一下 OpenJDK 1.6 源代码,你可以看到,实际上底层对象实际上无限期地保留了对底层可调用对象的引用(即只要存在对该对象的强引用,可调用对象就不能是 GCd)。1.7 也是如此。从 1.8 开始,对它的引用清空。但是,您无法控制任务的哪个实现将运行。ThreadPoolExecutorFutureFutureExecutorService

在实践中使用一个应该工作,因此一旦任务完成,对象就可以是GCD,并且当任务完成时,理智的实现应该失去对它的引用。严格来说,这仍然取决于执行器服务的实现。此外,使用弱引用可能会增加惊人的巨大开销。您最好只是显式清理占用大量内存的任何对象。相反,如果您不分配大型对象,那么我不会担心。WeakReferenceFutureCallableExecutorService

当然,这个讨论与如果你继续向列表中添加期货而不删除任何期货,你将遇到的内存泄漏完全不同。如果你这样做,即使使用也无济于事;您仍然会有内存泄漏。为此,只需循环访问列表并删除已经完成因此无用的期货。每次都这样做真的很好,除非你有一个非常大的队列大小,因为这非常快。WeakReference


答案 2
  1. runningTasks( Future ) 是否包含对任务对象的引用?--- 是的
  2. 它能维持多久?任务完成后,它是否仍保留它?---它持有任务引用,直到任务完成,之后它从ThreadPoolExecutor的队列中删除,也在FutureTask.finishCompletion()方法中,我们将callable(task)引用设置为null。FutureTask.finishCompletion() 在 FuturetTask 的 run 和 cancel 方法内部调用。因此,在运行和取消这两种情况下,将来都没有对任务的引用。
  3. 为了避免内存泄漏,我是否要注意删除添加到列表中的未来?--- 如果您使用的是 .,那么您是安全的。Future

如果您使用,则可能会遇到内存泄漏问题,因为或通常不会通知它已被取消,并且它会一直保留在队列中,直到其执行时间到来。对于简单的期货来说,这没什么大不了的,但对于.它可以在那里停留几秒钟,几分钟,几小时,几天,几周,几年或几乎无限期,这取决于它被安排的延迟。ScheduledFutureScheduledFuture.cancel()Future.cancel()ExecutorScheduledFutures

有关内存泄漏情况及其解决方案的示例的更多详细信息,请参阅我的其他答案