ExecutorService 超出范围时是否会被垃圾收集?

我问这个问题是因为我正在创建很多执行程序服务,虽然我可能已经在某个需要调查的地方存在内存泄漏,但我认为最近对以下代码的更改实际上使情况恶化了,因此我试图确认发生了什么:

I'm asking this question because I am creating a lot of executor services and while I may already have a memory leak somewhere that needs to be investigated, I think a recent change to the following code actually worsened it, hence I am trying to confirm what is going on:

@FunctionalInterface
public interface BaseConsumer extends Consumer<Path> {
    @Override
    default void accept(final Path path) {
        String name = path.getFileName().toString();
        ExecutorService service = Executors.newSingleThreadExecutor(runnable -> {
            Thread thread = new Thread(runnable, "documentId=" + name);
            thread.setDaemon(true);
            return thread;
        });
        Future<?> future = service.submit(() -> {
            baseAccept(path);
            return null;
        });
        try {
            future.get();
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        } catch (ExecutionException ex) {
            throw new RuntimeException(ex);
        }
    }

    void baseAccept(final Path path) throws Exception;
}

然后这个 Consumer<Path> 被另一个线程池调用(通常)N=2 个线程,我不确定这是否相关.

Then this Consumer<Path> gets called on another thread pool with (usually) N=2 threads, I am not sure if that is relevant.

问题是:ExecutorService 服务 是否超出范围并 在 BaseConsumer#accept 完成后收集垃圾?

The question is: Does the ExecutorService service go out of scope and get garbage collected once BaseConsumer#accept has finished?

推荐答案

BaseConsumer.accept() 完成后,ExecutorService 服务是否超出范围并收集垃圾?

Does the ExecutorService service go out of scope and get garbage collected once BaseConsumer.accept() has finished?

是的.

确实,相关的线程池也应该被垃圾回收......最终.

Indeed, the associated thread pool should also be garbage collected ... eventually.

Executors.newSingleThreadExecutor() 创建的 ExecutorServiceFinalizableDelegatedExecutorService 的一个实例.该类具有 finalize() 方法,该方法在包装的 ExecutorService 对象上调用 shutdown().如果所有未完成的任务都真正终止,服务对象将关闭其线程池.

The ExecutorService that is created by Executors.newSingleThreadExecutor() an instance of FinalizableDelegatedExecutorService. That class has finalize() method that calls shutdown() on the wrapped ExecutorService object. Provided that all outstanding tasks actually terminate, the service object will shut down its thread pool.

(AFAIK,没有具体说明.但它是根据源代码实现的,在 Java 6 及以上版本中.)

(AFAIK, this is not specified. But it is what is implemented according to the source code, in Java 6 onwards.)

是否添加 finally { service.shutdown();在 future.get() 的 try-catch 中帮助更快地检索资源?(不一定是垃圾收集服务对象).

Does adding a finally { service.shutdown(); } in the try-catch around future.get() help in retrieving resources quicker? (not necessarily garbage collecting the service object).

是的.调用 shutdown() 会在未完成的任务完成后立即释放线程.该过程会立即启动,而如果您只是将其留给垃圾收集器,则在调用终结器之前它不会启动.

Yes it does. Calling shutdown() causes the threads to be released as soon as the outstanding tasks complete. That procedure starts immediately, whereas if you just left it to the garbage collector it wouldn't start until the finalizer was called.

现在,如果资源只是普通"的 Java 对象,这无关紧要.但在这种情况下,您要回收的资源是一个 Java 线程,它具有相关的操作系统资源(例如本机线程)和一个重要的堆外内存块.所以这样做也许是值得的.

Now if the resources were just "ordinary" Java objects, this wouldn't matter. But in this case, the resource that you are reclaiming is a Java thread, and that has associate operating system resources (e.g. a native thread), and a non-trivial chunk of out-of-heap memory. So it is maybe worthwhile to do this.

但如果您希望对此进行优化,也许您应该创建一个长期存在的 ExecutorService 对象,并在多个消费者"实例之间共享它.

But if you are looking to optimize this, maybe you should be creating a long-lived ExecutorService object, and sharing it across multiple "consumer" instances.

相关文章