2012-10-15 2 views
8

Sto utilizzando un ThreadPoolExecutor per eseguire attività. Il back-end è un SynchronousQueue, quindi se l'esecutore sta già eseguendo il completamento di un'attività, genera RejectedExecutionException. Ecco un semplice caso di test:Il mio ThreadPoolExecutor perde memoria?

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) {     
     } 
    } 
    } 
} 

Il bahavious atteso è: Eseguire il lavoratore e dopo aver dormito per un secondo, stampare i (che rappresenta quanto spesso il lavoratore è stato eseguito finora) e la quantità di millisecondi dal momento che la lavoratore è stato creato. Quindi mi aspetto:

1 1015 
2 2015 
3 3016 
4 4017 

Questo funziona bene per un po ', ma dopo quasi un'ora:

2919 2922196 
2920 2942951 
2921 2990407 

Così la quantità di tempo che intercorre tra un'esecuzione dei lavoratori e quello successivo è di 20 secondi (2919-> 2920) e 38 secondi (2920-> 2921) e così via. Tutto diventa estremamente lento e la jvm impiega molto tempo nella raccolta dei rifiuti. Alla fine (dopo alcuni giorni) mi imbatto in un OutOfMemoryError.

L'ho eseguito con -Xmx8M (presumo che l'effetto venga visualizzato molto più tardi con più spazio heap) su JVM 1.7.0_07 di Oracle su un computer Linux a 64 bit. Apprezzerei qualsiasi suggerimento, ma probabilmente mi manca l'ovvio.

+1

Forse lo stack si sta affollando con RejectedExecutionExceptions dopo un po '(anche se ho pensato che gli oggetti di eccezione siano stati riutilizzati dopo un po')? Hai provato a profilare la tua app? – assylias

+0

@assylias Beh, ho profilato l'app ovviamente, ma ho guardato stupidamente solo lo spazio del mucchio e le generazioni sopravviventi. In questo momento sto eseguendo il profiler per verificare, ma a prima vista le eccezioni di RejctedExectution non sembrano essere il colpevole. Anche se lo sono, devo cogliere queste eccezioni e non dovrebbe esaurire la memoria alla fine della giornata, giusto? –

+0

Questo spiegherebbe la maggiore quantità di GC ma non l'OOME ... – assylias

risposta

2

Si può provare a modificare l'istanza ThreadPoolExecutor. Devi solo aggiungere un argomento alla funzione di costruzione per utilizzare uno RejectExecutionHandler che eliminerà automaticamente le attività rifiutate.

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);     
    } 
} 

Quindi se il problema deriva da ripetute RejectedExecutionException (e penso così), si evitarlo.

+1

+1 questo riduce notevolmente la quantità di attività del GC come mostrato da '-verbose: gc -XX: + PrintGCDetails' – DNA