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

ski*_*iwi 19 java multithreading garbage-collection executorservice java-8

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

@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;
}
Run Code Online (Sandbox Code Playgroud)

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

问题是:一旦完成,是否ExecutorService service超出范围收集垃圾BaseConsumer#accept

Ste*_*n C 19

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

是.

实际上,关联的线程池也应该是垃圾收集......最终.

ExecutorService由创建Executors.newSingleThreadExecutor()的实例FinalizableDelegatedExecutorService.该类具有finalize()调用shutdown()包装ExecutorService对象的方法.如果所有未完成的任务实际终止,则服务对象将关闭其线程池.

(AFAIK,这没有指定.但它是根据源代码实现的,在Java 6以后.)


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

是的,它确实.shutdown()一旦未完成的任务完成,调用将导致线程被释放.该过程立即开始,而如果你只是将它留给垃圾收集器,它将不会启动,直到调用终结器.

现在,如果资源只是"普通的"Java对象,那就无所谓了.但在这种情况下,您要回收的资源是Java线程,并且具有关联的操作系统资源(例如本机线程)和非平凡的堆外存储器块.所以这可能是值得的.

但是如果你想要优化它,也许你应该创建一个长期存在的ExecutorService对象,并在多个"消费者"实例中共享它.

  • 添加`finally {service.shutdown(); 在`future.get()的try-catch中``帮助检索*资源*更快?(不一定是收集`service`对象的垃圾) (2认同)

Pet*_*rey 7

我想让执行发生在命名线程上,它与更容易记录有关.在任何一种情况下,此代码都应该有效.

你可以更简单/更快地做到这一点

Thread t = Thread.currentThread();
String name = t.getName();
try {
    t.setName("My new thread name for this task");
    // do task
} finally {
    t.setName(name);
}
Run Code Online (Sandbox Code Playgroud)

这样,您可以使用命名线程而无需创建新线程.