2012-10-15 70 views
8

我正在使用ThreadPoolExecutor來運行任務。後端是SynchronousQueue,所以如果執行程序已經執行任務,則會拋出RejectedExecutionException。這裏有一個簡單的測試案例:我的ThreadPoolExecutor是否泄漏內存?

public class ExecutorTest { 

    final static Worker worker = new Worker(); 

    public static void main(String[] args) { 
    ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>()); 

    while (true) { 
     try {     
      executor.execute(worker);     
     }catch (RejectedExecutionException e) {     
     } 
    }   
    } 

    static class Worker implements Runnable { 

    private int i = 0; 
    private long start = System.currentTimeMillis(); 

    @Override 
    public void run() { 
     try { 
      Thread.sleep(1000); 
      System.out.println(++i + " " + (System.currentTimeMillis() - start)); 
     } catch (InterruptedException ex) {     
     } 
    } 
    } 
} 

預期bahavious是:執行的工人,睡了一秒鐘後,打印出我(代表多久工人迄今已執行)和毫秒的,因爲量工人被創建。 所以我很期待:

1 1015 
2 2015 
3 3016 
4 4017 

這一段時間工作正常,但幾乎小時後:

2919 2922196 
2920 2942951 
2921 2990407 

所以一個工人執行和下一個之間的時間量爲20秒(2919-> 2920)和38秒(2920-> 2921)等等。一切都變得非常緩慢,jvm花費大量時間進行垃圾收集。最後(幾天之後),我遇到了OutOfMemoryError。

我在64位Linux機器上的Oracle JVM 1.7.0_07上使用-Xmx8M(我假設效果出現得更晚,堆空間更多)運行此操作。我會很欣賞任何指針,但可能我只是想念明顯。

+1

也許堆會在一段時間後變得擁擠與RejectedExecutionExceptions(儘管我認爲異常對象被重用一段時間後)?你有沒有試圖分析你的應用程序? – assylias

+0

@assylias當然,我描繪了應用程序,但愚蠢地只看堆空間和gc倖存的一代。我現在正在運行分析器來檢查,但乍一看,RejctedExectutionException似乎不是罪魁禍首。即使它們是,我必須趕上這些例外情況,並且不應該在一天結束時耗盡內存,對吧? –

+0

這將解釋增加的GC數量,但不是OOME ...... – assylias

回答

2

您可以嘗試修改ThreadPoolExecutor實例。您只需向構造函數添加一個參數即可使用RejectExecutionHandler,它將默默放棄拒絕的任務。

public static void main(String[] args) { 
    ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy()); 

    while (true) { 
    executor.execute(worker);     
    } 
} 

所以如果您的問題來自於重複RejectedExecutionException(我是這麼認爲的),你會避免它。

+1

+1這大大減少了GC活動的數量,如'-verbose:gc -XX:+ PrintGCDetails'所示。 – DNA