Предоставляет ли ExecutorService сбор мусора, если он выходит за рамки?

Я задаю этот вопрос, потому что я создаю множество сервисов-исполнителей, и хотя у меня уже может быть утечка памяти где-то, что нужно исследовать, я думаю, что недавнее изменение на следующий код фактически ухудшило его, следовательно, я пытаясь подтвердить, что происходит:

@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 потоками, я не уверен, что это актуально.

Возникает вопрос: выходит ли ExecutorService service из области видимости и собирает мусор после завершения BaseConsumer#accept?

Ответы

Ответ 1

Вызывается ли служба ExecutorService из области видимости и собирается мусор после завершения BaseConsumer.accept()?

Да.

В самом деле, связанный пул потоков также должен быть собран мусором... в конце концов.

ExecutorService, созданный Executors.newSingleThreadExecutor() экземпляром FinalizableDelegatedExecutorService. Этот класс имеет метод finalize(), который вызывает shutdown() на обернутом ExecutorService объекте. При условии, что все выдающиеся задачи фактически прекратятся, объект службы отключит пул потоков.

(AFAIK, это не указано, но это то, что реализовано в соответствии с исходным кодом в Java 6 и далее.)


Добавляет ли наконец {service.shutdown(); } в try-catch вокруг функции future.get() в поиске ресурсов быстрее? (не обязательно сбор мусора объекта службы).

Да, да. Вызов shutdown() приводит к тому, что потоки будут выпущены сразу после завершения выдающихся задач. Эта процедура начинается немедленно, тогда как если вы просто оставите ее в сборщике мусора, она не начнется, пока не будет вызван финализатор.

Теперь, если ресурсы были просто "обычными" объектами Java, это не имело бы значения. Но в этом случае ресурс, который вы восстанавливаете, является потоком Java и который связывает ресурсы операционной системы (например, собственный поток) и нетривиальный кусок памяти из-кучи. Поэтому, возможно, стоит сделать это.

Но если вы хотите оптимизировать это, возможно, вам следует создать долгоживущий объект ExecutorService и поделиться им с несколькими экземплярами "потребителя".

Ответ 2

Я хочу, чтобы выполнение выполнялось по именованному потоку, оно связано с тем, что его легче записывать. В любом случае этот код должен работать.

Вы можете сделать это намного проще/быстрее

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

Таким образом вы можете использовать именованный поток без создания нового.