2011-02-06 20 views
9

Tôi đang gửi Callable đối tượng vào một số ThreadPoolExecutor và dường như chúng được gắn bó trong bộ nhớ.Làm thế nào để đảm bảo thu gom rác của FutureTask được gửi đến ThreadPoolExecutor và sau đó bị hủy?

Nhìn vào đống đổ với công cụ MAT cho Eclipse thấy rằng các đối tượng Callable đang được tham chiếu bởi một callable biến FutureTask$Sync 's. Điều đó FutureTask$Sync được tham chiếu bởi một biến số đồng bộ của FutureTask. Điều đó FutureTask được tham chiếu bởi số của FutureTask$Sync này $ 0.

Tôi đã đọc xung quanh về vấn đề này (here, here, và trên SO) và nó có vẻ như FutureTask rằng callable được gói trong khi ThreadPoolExecutor 's nộp() giữ một tham chiếu đến callable mãi mãi.

Điều tôi đang bối rối là làm thế nào để đảm bảo rằng FutureTask bị thu gom rác để nó không tiếp tục giữ được bộ nhớ trong bộ nhớ, và giữ bất cứ thứ gì có thể gọi được trong bộ nhớ?

Chỉ để cung cấp thêm chi tiết về tình huống cụ thể của tôi, tôi đang cố gắng triển khai ThreadPoolExecutor theo cách cho phép tất cả các tác vụ đã gửi bị hủy nếu cần. Tôi đã thử một số phương pháp khác nhau mà tôi tìm thấy trên SO và các nơi khác, chẳng hạn như tắt hoàn toàn trình điều hành (với shutdown(), shutdownNow() v.v.) và cũng giữ danh sách tương lai quay lại submit() và hủy cuộc gọi trên tất cả chúng rồi xóa danh sách tương lai. Lý tưởng nhất là tôi không muốn phải tắt nó, và chỉ cancel() và rõ ràng khi cần thiết.

Tất cả các phương pháp này dường như không tạo nên sự khác biệt. Nếu tôi gửi một cuộc gọi đến các hồ bơi, có một cơ hội tốt nó sẽ kết thúc gắn bó xung quanh.

Tôi đang làm gì sai?

Cảm ơn.

Edit:

Theo yêu cầu, ở đây là các nhà xây dựng cho ThreadPoolExecutor.

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { 
    super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); 
} 

Sau khi kiểm tra thêm, tôi có thể thấy rằng nếu tôi để các tác vụ đã được gửi đến ThreadPoolExecutor kết thúc, thì không có rò rỉ.Nếu tôi cố gắng hủy chúng trong anyway như:

shutdownNow() 

Hoặc tiết kiệm một tham chiếu đến tương lai và kêu gọi hủy bỏ vào nó sau:

Future referenceToCancelLater = submit(task); 
... 
referenceToCancelLater.cancel(false); 

Hoặc bằng cách loại bỏ chúng ra khỏi hàng đợi với các phương pháp như:

getQueue.drainTo(someList) 

hoặc

getQueue.clear() 

hoặc Looping thông qua tài liệu tham khảo lưu vào tương lai và gọi:

getQueue.remove(task) 

Bất kỳ của những trường hợp gây ra FutureTask để dính vào xung quanh như đã mô tả ở trên. Vì vậy, câu hỏi thực sự trong tất cả những điều này là làm thế nào để tôi đúng cách hủy bỏ hoặc loại bỏ các mặt hàng từ một ThreadPoolExecutor để các FutureTask là rác thu thập và không bị rò rỉ mãi mãi?

+1

đăng mã của bạn, theo cách khó loại bỏ: 'ThreadPoolExecutor.getQueue(). Remove (future)' sẽ thực hiện thủ thuật – bestsss

+0

Có một số thảo luận về việc liệu getQueue() có nên được sử dụng theo cách đó hay không. Có thực sự là một nhược điểm để làm theo cách đó? – cottonBallPaws

+0

@bestsss Tôi đã hy vọng tôi sẽ không nhận được yêu cầu đó;) Mã có rất nhiều đang xảy ra và tôi không chắc chắn những phần có liên quan đến vấn đề này. Tôi đã hy vọng để có được một ý thức chung về những gì có thể gây ra một rò rỉ liên quan đến FutureTask. Có một phần cụ thể mà bạn muốn xem không? – cottonBallPaws

Trả lời

1

tôi không thể có được bất cứ điều gì để làm việc vì vậy tôi đã đưa ra các giải pháp sau đây. Đây là một tổng quan thô: Tôi tạo ra một mảng trong ThreadPoolExecutor mà theo dõi các runnables trong hàng đợi. Sau đó, khi tôi cần phải hủy bỏ hàng đợi, tôi lặp lại và gọi một phương thức hủy bỏ trên mỗi runnables. Tôi trường hợp của tôi, tất cả các runnables là một lớp tùy chỉnh tôi tạo ra và phương pháp hủy của họ chỉ đơn giản là thiết lập một lá cờ bị hủy bỏ. Khi hàng đợi đưa lên kế tiếp để xử lý, trong khi chạy runnable nó sẽ thấy nó đã bị hủy bỏ và bỏ qua công việc thực tế.

Vì vậy, tất cả các runnables sau đó chỉ cần được xả ra nhanh chóng từng người một vì nó thấy nó đã bị hủy bỏ.

Có lẽ không phải là giải pháp tốt nhất, nhưng nó hoạt động cho tôi và nó không bị rò rỉ bộ nhớ.

+0

Xin chào, tôi nghĩ rằng tôi đang gặp vấn đề tương tự. Bạn đã tìm ra cách để giải quyết vấn đề một cách sạch hơn;)? Trên thực tế, cần tồn tại một số lớp Java để xử lý việc xóa các chuỗi đã hết hạn đó. Chúc mừng – Simone

+0

@Simone tiếc là không, tôi không thể tìm thấy thứ gì đó được tích hợp sẵn. Tôi vẫn đang sử dụng phương pháp trên. Nó hoạt động tốt mặc dù, chỉ mất một số nỗ lực để có được thiết lập. – cottonBallPaws

+0

Cảm ơn bạn đã trả lời. Sau đó, phương thức hủy (lớp Runnable) có giải phóng bộ nhớ được chỉ định luồng hay bạn đã làm thủ công? – Simone

1

Là một công trình xung quanh bạn có thể làm một cái gì đó như:

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

Điều gì sẽ là đại biểu trong trường hợp này? – cottonBallPaws

+1

Có thể gọi của bạn đang giữ trạng thái bạn muốn thu gom rác. – Tom

+0

điều này không giải quyết được gì. Vấn đề không phải là 'ThreadPoolExecutor', b/c nó, một mình, không bị rò rỉ các nhiệm vụ được thực hiện – bestsss

4

Theo this post, bạn có thể gọi purge trên người thực hiện.

+0

nó nói "Cố gắng để loại bỏ khỏi hàng đợi công việc tất cả các nhiệm vụ trong tương lai đã bị hủy bỏ. Phương pháp này có thể hữu ích như một hoạt động khai hoang lưu trữ, không có tác động khác về chức năng. Nhiệm vụ bị hủy không bao giờ được thực hiện, nhưng có thể tích lũy trong hàng đợi công việc cho đến khi các chuỗi công nhân có thể chủ động loại bỏ chúng. Gọi phương thức này thay vì cố gắng loại bỏ chúng ngay bây giờ. Tuy nhiên, phương pháp này có thể không xóa nhiệm vụ khi có sự can thiệp của các luồng khác. " có nghĩa là nếu nhiệm vụ đã chạy nhưng rơi vào bế tắc thì nó sẽ không bị giết? – HoaPhan

0

Tham khảo: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html

Một tương lai thể hiện kết quả của một tính toán không đồng bộ. Nếu kết quả không được lấy ra bằng cách sử dụng phương pháp nhận được sau đó rò rỉ bộ nhớ xảy ra!

Nếu bạn không muốn sử dụng kết quả không đồng bộ, hãy sử dụng Runnable install Callable.

Các vấn đề liên quan