Domanda

Riesci a individuare il bug? Questo genererà un java.lang.OutOfMemoryError.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestTheads {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(1);
        while(true) {
            executorService.submit(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }
                }
            });
        }
    }

}

Il bug è che io chiamo executorService.submit() invece di executorService.execute(), perché submit() restituisce un oggetto Future sto ignorando. Con execute(), questo programma sarà effettivamente funzionare per sempre.

Tuttavia, non sempre hanno il lusso di avere un metodo execute(), come quando si utilizza un ScheduledExecutorService:

public static void main(String[] args) {
    // this will FAIL because I ignore the ScheduledFuture object
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
    while(true) {
        executorService.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
            }
        }, 1, 1, TimeUnit.SECONDS);
    }
}

Quello che è uno deve fare con le mansioni che non restituiscono nulla, solo calcolare?

Tutte le idee sarebbe grato apprezzato!

Modifica :. ThreadPoolExecutors purge() sembrava promettente, ma solo purghe compiti annullato

È stato utile?

Soluzione

L'oggetto Future che viene restituito è fortemente riferimento il ExecutorService solo fino a quando non viene eseguito. (In realtà è un'istanza FutureTask che i delegati al vostro Runnable.) Una volta eseguito, sarà garbage collection, perché il chiamante non ha alcun riferimento ad esso. In altre parole, il problema di memoria non ha nulla a che fare con il trattamento del Future.

Se si esegue fuori la memoria, è perché la coda di lavoro ha milioni di operazioni in coda. Come con qualsiasi coda, a meno che il tasso medio di consumo supera il tasso medio di produzione, la coda si riempirà. Il contenuto della coda consumano memoria.

Usa una coda limitata, che sarà efficace, acceleratore operazione messa in coda, o di ottenere più memoria.

Questo codice verrà eseguito "per sempre":

  ExecutorService executorService = Executors.newFixedThreadPool(1);
  while(true) {
    executorService.submit(new Runnable() {
      public void run() {
        try {
          Thread.sleep(10);
        } catch (InterruptedException e) { }
      }
    });
    Thread.sleep(12);
  }

La differenza non è nel trattamento delle istanze Future risultanti, ma che i compiti vengono accodati ad un tasso a cui possono essere elaborati.

Altri suggerimenti

E 'non è tanto il Futures che vengono restituiti che è il tuo problema. Il problema è che, per ogni Runnable si invia, l'ExecutorService memorizzerà loro di essere in grado di elaborare in un secondo momento. Ogni volta che si richiama il metodo di invio con un Runnable (o futuri), l'ExecutorService spingerà che eseguibile a una coda dei lavoratori. Il Runnable siederà lì fino a quando un thread può scegliere che Runnable dalla coda (il tempo più tardi). Se tutti i thread di lavoro sono occupati poi l'ExecutorService semplicemente mettere il eseguibile a detta coda.

Quindi, il problema è che avete un solo thread cercando di tirare di una coda che viene infinititely aggiunto da un altro thread. Il suo essere aggiunto molto più veloce il thread di lavoro in grado di elaborare ogni eseguibile.

Modifica:. L'esempio di codice ho dato come nos eluso a in effetti gettare un RejectedExecutionException, in modo che il meccanismo di limitazione dovrebbe essere leggermente diverso se si dovesse scegliere

Per quanto riguarda una soluzione migliore, come ho detto nel mio commento; Se vi aspettate di riempire l'ExecutorService in tal modo un che i thread di lavoro non possono tenere il passo con la coda, è possibile serializzare e deserializzare le richieste così come arrivano (costruire il proprio ThreadPoolExecutor), ma vorrei fare in modo la necessità di un tale caso è assolutamente necessario.

Tenete a mente dopo che il lavoro è stato fatto quelle future di saranno disgarded e garbage collection. Quindi, se si fa un futuro al secondo ed esegue in meno di un secondo il futuro stesso verrà rimosso e non si avrà un problema di memoria. Ma se si sta facendo un futuro un secondo e i fili fanno un futuro ogni 3 secondi, che trarrà e il rilascio.

Edit: ho profilato il mucchio di programma che si sta eseguendo e il problema è esattamente questo. Il FutureTask essere creato dal ExecutorService è seduto sulla coda dei lavoratori fino a quando le scelte thread di lavoro fuori

Class Name                                                       | Shallow Heap | Retained Heap | Percentage 
------------------------------------------------------------------------------------------------------------
java.util.concurrent.ThreadPoolExecutor @ 0x78513c5a0            |          104 | 2,051,298,872 |     99.99% 
|- java.util.concurrent.LinkedBlockingQueue @ 0x785140598        |           80 | 2,051,298,216 |     99.99% 
|  |- java.util.concurrent.LinkedBlockingQueue$Node @ 0x785142dd8|           32 | 2,051,297,696 |     99.99% 
------------------------------------------------------------------------------------------------------------

L'analisi mucchio va avanti un po ', ci sono molti LinkedBlockingQueue $ nodo di come si può immaginare

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top