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.
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
@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? –
Questo spiegherebbe la maggiore quantità di GC ma non l'OOME ... – assylias