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

时间:2014-04-11 13:53:13

标签: java runnable future executorservice

我们假设我们有以下代码:

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

我的问题是:

  1. runningTasks是否包含对task对象的引用?
  2. 它持有多长时间?任务完成后是否还能保留它?
  3. 为了避免内存泄漏,我是否必须小心删除添加到列表中的未来?

2 个答案:

答案 0 :(得分:2)

在执行者或Future对象持有对它的引用之前是一个实现细节。因此,如果您的任务使用大量内存而不必担心内存使用情况,则应在任务完成之前明确清理任务。

如果查看ThreadPoolExecutor的{​​{3}},您可以看到实际上底层Future对象实际上无限期地保留了对底层可调用对象的引用(即只要存在对Future对象的强引用,可调用的不能是GCd)。对于1.7也是如此。从1.8开始,对的引用被删除。但是,您无法控制将运行ExecutorService任务的实现。

使用WeakReference实际上应该作为Future工作,因此Callable对象可以在任务完成后成为GCd,并且ExecutorService的合理实现应该失去参考当任务完成时。严格来说,这仍然取决于ExecutorService的实现。此外,使用WeakReference可能会增加惊人的大开销。只需明确清理占用大量内存的任何对象,你会好得多。相反,如果你没有分配大型物体,那我就不用担心了。

当然,如果你不断删除任何期货而不断添加期货,那么这个讨论就完全不同于你将会遇到的内存泄漏。如果你这样做,即使使用WeakReference也无济于事;你还会有内存泄漏。为此,只需遍历列表并删除已经完成的未来,因此没有用。每次都这样做真的很好,除非你有一个非常大的队列大小,因为这很快。

答案 1 :(得分:-1)

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

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

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