Предоставляет ли 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);
}
Таким образом вы можете использовать именованный поток без создания нового.