Quali sono le cause java.lang.OutOfMemoryError: impossibile creare un nuovo thread nativo

1. Introduzione

In questo tutorial, discuteremo la causa e le possibili soluzioni di java.lang.OutOfMemoryError: impossibile creare un nuovo errore di thread nativo .

2. Capire il problema

2.1. Causa del problema

La maggior parte delle applicazioni Java è di natura multithread , costituita da più componenti, che eseguono attività specifiche ed eseguite in thread diversi. Tuttavia, il sistema operativo sottostante (OS) impone un limite al numero massimo di thread che un'applicazione Java può creare.

La JVM genera un errore di impossibile creare un nuovo thread nativo quando la JVM chiede al sistema operativo sottostante un nuovo thread e il sistema operativo non è in grado di creare nuovi thread del kernel noti anche come thread del sistema operativo o di sistema . La sequenza degli eventi è la seguente:

  1. Un'applicazione in esecuzione all'interno della JVM (Java Virtual Machine) richiede un nuovo thread
  2. Il codice nativo JVM invia una richiesta al sistema operativo per creare un nuovo thread del kernel
  3. Il sistema operativo tenta di creare un nuovo thread del kernel che richiede l'allocazione della memoria
  4. Il sistema operativo rifiuta l'allocazione della memoria nativa perché entrambi
    • Il processo Java richiedente ha esaurito lo spazio degli indirizzi di memoria
    • Il sistema operativo ha esaurito la sua memoria virtuale
  5. Il processo Java restituisce quindi java.lang.OutOfMemoryError: impossibile creare un nuovo errore di thread nativo

2.2. Modello di allocazione dei thread

Un sistema operativo ha in genere due tipi di thread: thread utente (thread creati da un'applicazione Java) e thread del kernel . I thread utente sono supportati sopra i thread del kernel ei thread del kernel sono gestiti dal sistema operativo.

Tra di loro, ci sono tre relazioni comuni:

  1. Many-To-One : molti thread utente vengono mappati su un singolo thread del kernel
  2. One-To-One : una mappa del thread utente su un thread del kernel
  3. Many-To-Many - Molti thread utente eseguono il multiplex su un numero minore o uguale di thread del kernel

3. Riprodurre l'errore

Possiamo facilmente ricreare questo problema creando thread in un ciclo continuo e quindi attendere i thread:

while (true) { new Thread(() -> { try { TimeUnit.HOURS.sleep(1);     } catch (InterruptedException e) { e.printStackTrace(); } }).start(); }

Dal momento che stiamo trattenendo ogni thread per un'ora, mentre ne creiamo continuamente di nuovi, raggiungeremo rapidamente il numero massimo di thread dal sistema operativo.

4. Soluzioni

Un modo per risolvere questo errore consiste nell'aumentare la configurazione del limite di thread a livello di sistema operativo.

Tuttavia, questa non è una soluzione ideale perché OutOfMemoryError indica probabilmente un errore di programmazione. Diamo un'occhiata ad altri modi per risolvere questo problema.

4.1. Sfruttare Executor Service Framework

Sfruttare il framework del servizio esecutore di Java per l'amministrazione dei thread può risolvere questo problema in una certa misura. Il framework del servizio esecutore predefinito, o una configurazione esecutore personalizzato, può controllare la creazione del thread.

Possiamo usare il metodo Executors # newFixedThreadPool per impostare il numero massimo di thread che possono essere usati alla volta:

ExecutorService executorService = Executors.newFixedThreadPool(5); Runnable runnableTask = () -> { try { TimeUnit.HOURS.sleep(1); } catch (InterruptedException e) { // Handle Exception } }; IntStream.rangeClosed(1, 10) .forEach(i -> executorService.submit(runnableTask)); assertThat(((ThreadPoolExecutor) executorService).getQueue().size(), is(equalTo(5)));

Nell'esempio precedente, creiamo prima un pool di thread fissi con cinque thread e un'attività eseguibile che fa attendere i thread per un'ora. Inoltriamo quindi dieci attività di questo tipo al pool di thread e asseriamo che cinque attività sono in attesa nella coda del servizio esecutore.

Poiché il pool di thread ha cinque thread, può gestire un massimo di cinque attività in qualsiasi momento.

4.2. Acquisizione e analisi del dump del thread

L'acquisizione e l'analisi del dump del thread è utile per comprendere lo stato di un thread.

Diamo un'occhiata a un dump di thread di esempio e vediamo cosa possiamo imparare:

L'istantanea del thread precedente proviene da Java VisualVM per l'esempio presentato in precedenza. Questa istantanea dimostra chiaramente la creazione di thread continui.

Una volta identificata la creazione di thread continui, possiamo acquisire il dump del thread dell'applicazione per identificare il codice sorgente che crea i thread:

Nello snapshot sopra, possiamo identificare il codice responsabile della creazione del thread. Ciò fornisce informazioni utili per adottare misure appropriate.

5. conclusione

In questo articolo, abbiamo appreso di java.lang.OutOfMemoryError: impossibile creare un nuovo errore di thread nativo e abbiamo visto che è causato da un'eccessiva creazione di thread in un'applicazione Java.

Abbiamo esplorato alcune soluzioni per affrontare e analizzare l'errore esaminando il framework ExecutorService e l'analisi del thread dump come due misure utili per affrontare questo problema.

Come sempre, il codice sorgente dell'articolo è disponibile su GitHub.