2011-02-06 3 views
9

Я отправляю Callable объектов в ThreadPoolExecutor, и они, кажется, торчат в памяти.Как обеспечить сбор мусора в FutureTask, который отправлен в ThreadPoolExecutor и затем отменен?

Глядя дампа кучи с помощью инструмента MAT Затмения увидеть, что Callable объекты которые ссылаются на отзывной переменной FutureTask$Sync «s. То, что FutureTask$Sync ссылается на переменную FutureTaskс переменной частотой. Это FutureTask ссылается на FutureTask$Syncэтой переменной $ 0.

Я прочитал вокруг этого (here, here, и на SO), и кажется, что FutureTask что вызываемый заворачивают в на ThreadPoolExecutor «s представить() содержит ссылку на Вызываемый навсегда.

В чем я смущен, так это то, как обеспечить, чтобы FutureTask собирал мусор, чтобы он не продолжал удерживать вызываемый в памяти и удерживать в памяти все, что вызываемый может содержать в памяти?

Чтобы подробно рассказать о моей конкретной ситуации, я пытаюсь реализовать ThreadPoolExecutor таким образом, чтобы все предоставленные задачи можно было отменить при необходимости. Я пробовал несколько разных методов, которые я нашел в SO и в других местах, например, полностью отключив исполнителя (с shutdown(), shutdownNow() и т. Д.), А также сохраняя список возвратов фьючерсов на submit() и вызывая отмену для всех них, а затем очищая список фьючерсы. В идеале я бы хотел не закрывать его, а просто cancel() и убирать при необходимости.

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

Что я делаю неправильно?

Спасибо.

Edit:

В соответствии с просьбой, вот конструктор для 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 собирал мусор и не просачивался навсегда?

+1

Отправляйте свой код с жесткого пути удаления: 'ThreadPoolExecutor.getQueue(). Remove (future)' будет делать трюк – bestsss

+0

Существует некоторое обсуждение вопроса о том, следует ли использовать метод getQueue(). Есть ли недостаток в этом? – cottonBallPaws

+0

@bestsss Я надеялся, что меня не спросят, что;) В коде много чего происходит, и я не уверен, какие части имеют отношение к этой проблеме. Я надеялся получить общее представление о том, что может вызвать утечку в отношении FutureTask. Есть ли какая-то конкретная деталь, которую вы хотите увидеть? – cottonBallPaws

ответ

1

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

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

Возможно, это не самое большое решение, но оно работает для меня, и это не утечка памяти.

+0

Привет, я думаю, у меня такая же проблема. Вы выяснили, как решить проблему в чистом виде?)? На самом деле, должны существовать некоторые Java-классы, которые заботятся об удалении этих истекших потоков. Cheers – Simone

+0

@ Симон, к сожалению, нет, я не смог найти что-то более встроенное. Я все еще использую вышеупомянутый метод. Он работает нормально, хотя, просто приложил некоторые усилия, чтобы получить настройки. – cottonBallPaws

+0

Спасибо за ответ. Затем, способ отмены (класс Runnable) освобождает выделенную потоком память или вы делаете это вручную? – Simone

1

В работе вокруг вы могли бы сделать что-то вроде:

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; 
     } 
    } 
} 
+0

Что было бы делегатом в этом случае? – cottonBallPaws

+1

Ваш вызываемый, который держит состояние, что вы хотите, чтобы собранный мусор. – Tom

+0

ничего не решает. Проблема заключается не в том, что «ThreadPoolExecutor», b/c, он сам по себе не теряет выполняемых задач. – bestsss

4

Согласно this post, вы можете позвонить по телефону purge об исполнителе.

+0

в нем говорится: «Пытается удалить из рабочей очереди все будущие задачи, которые были отменены. Этот метод может быть полезен в качестве операции восстановления хранилища, которая не имеет другого влияния на функциональность. Отмененные задачи никогда не выполняются, но могут накапливаться в рабочих очередях пока рабочие потоки не смогут их активно удалять. Вместо этого этот метод пытается удалить их. Однако этот метод может не удалять задачи при наличии помех другими потоками ». означает ли это, если задача уже запущена, но попадает в тупик, она не будет убита? – HoaPhan

0

См: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html

Будущее представляет собой результат асинхронных вычислений. Если результат не будет получен с помощью метода get, произойдет утечка памяти!

Если вы не хотите использовать асинхронный результат, используйте Runnable install Callable.