Как обеспечить сборку мусора для FutureTask, который передается ThreadPoolExecutor, а затем отменяется?
Я отправляю Callable
возражает против ThreadPoolExecutor
и они, кажется, торчат в памяти.
Глядя на дамп кучи с помощью инструмента MAT для Eclipse, вы увидите, что Callable
ссылки на объекты FutureTask$Sync
вызываемая переменная. Тот FutureTask$Sync
ссылается на FutureTask
Синхронная переменная. Тот FutureTask
ссылается на FutureTask$Sync
Это переменная $0.
Я читал об этом ( здесь, здесь и на SO), и кажется, FutureTask
что вызываемый обернут на ThreadPoolExecutor
Функция submit() всегда содержит ссылку на вызываемый объект.
Что меня смущает, так это то, как FutureTask
собирает мусор, чтобы он не продолжал удерживать вызываемое в памяти, и хранить что-либо, что вызываемое может быть в памяти?
Просто чтобы дать более подробную информацию о моей конкретной ситуации, я пытаюсь реализовать ThreadPoolExecutor
таким образом, что позволяет отменить все представленные задачи при необходимости. Я пробовал несколько различных методов, которые я нашел в SO и других местах, таких как полное выключение исполнителя (с помощью shutdown()
, shutdownNow()
и т.д.), а также ведение списка фьючерсов, возвращаемых submit()
и вызвать отмену на всех них, а затем очистить список фьючерсов. В идеале я хотел бы не выключать его, а просто cancel()
и очистить при необходимости.
Все эти методы, кажется, не имеют значения. Если я отправлю вызов в пул, есть большая вероятность, что он останется без дела.
Что я делаю неправильно?
Благодарю.
Редактировать:
Как и требовалось, здесь находится конструктор для ThreadPoolExecutor.
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
После дальнейшего тестирования я вижу, что если я позволю завершить задачи, которые были переданы в ThreadPoolExecutor, то утечки не будет. Если я попытаюсь отменить их в любом случае, такие как:
shutdownNow()
Или сохраните ссылку на будущее и позвоните, чтобы отменить ее позже:
Future referenceToCancelLater = submit(task);
...
referenceToCancelLater.cancel(false);
Или удалив их из очереди такими методами:
getQueue.drainTo(someList)
или же
getQueue.clear()
или перебирая сохраненные ссылки на фьючерсы и вызывая:
getQueue.remove(task)
Любой из этих случаев вызывает FutureTask, как описано выше.
Таким образом, реальный вопрос во всем этом заключается в том, как правильно отменить или удалить элементы из ThreadPoolExecutor, чтобы FutureTask собирал мусор, а не просачивался вечно?
4 ответа
Я не мог заставить что-либо работать, поэтому я нашел следующее решение. Вот приблизительный обзор: я создал массив в ThreadPoolExecutor, который отслеживал runnables, которые были в очереди. Затем, когда мне нужно было отменить очередь, я перебрал и вызвал метод отмены для каждого из исполняемых файлов. В моем случае все эти runnables были пользовательским классом, который я создал, и их метод cancel просто устанавливал флаг отмены. Когда очередь запускает следующую для обработки, при запуске runnable она видит, что она отменена, и пропускает фактическую работу.
Таким образом, все runnables тогда просто вымываются быстро один за другим, поскольку это видит, что это было отменено.
Возможно, не самое лучшее решение, но оно работает для меня и не пропускает память.
Согласно этому посту, вы можете вызвать чистку на исполнителя.
В качестве обходного пути вы могли бы сделать что-то вроде:
class ClearingCallable<T> implements Callable<T> {
Callable<T> delegate;
ClearingCallable(Callable<T> delegate) {
this.delegate = delegate;
}
T call() {
try {
return delegate.call();
} finally {
delegate = null;
}
}
}
См. https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html
Будущее представляет собой результат асинхронных вычислений. Если результат не будет получен с использованием метода get, произойдет утечка памяти!
Если вы не хотите использовать асинхронный результат, используйте Runnable install Callable.