Hvad forårsager java.lang.OutOfMemoryError: ude af stand til at oprette ny indfødt tråd

1. Introduktion

I denne vejledning diskuterer vi årsagen til og mulige løsninger på java.lang.OutOfMemoryError: ude af stand til at oprette ny native tråd fejl.

2. Forstå problemet

2.1. Årsag til problemet

De fleste Java-applikationer er multitrådede, der består af flere komponenter, der udfører specifikke opgaver og udføres i forskellige tråde. Dog den underliggende operativsystem (OS) pålægger et loft på det maksimale antal tråde som et Java-program kan oprette.

JVM kaster en ude af stand til at oprette ny indfødt tråd fejl, når JVM beder det underliggende operativsystem om en ny tråd, og operativsystemet er ude af stand til at oprette nye kernetråde, også kendt som OS eller systemtråde. Begivenhedens rækkefølge er som følger:

  1. Et program, der kører i Java Virtual Machine (JVM), anmoder om en ny tråd
  2. Den oprindelige JVM-kode sender en anmodning til operativsystemet om at oprette en ny kernetråd
  3. Operativsystemet forsøger at oprette en ny kernetråd, der kræver hukommelsestildeling
  4. OS nægter allokeret hukommelsestildeling, fordi begge
    • Den anmodende Java-processen har opbrugt sit hukommelsesadresseområde
    • OS har opbrugt sin virtuelle hukommelse
  5. Java-processen returnerer derefter java.lang.OutOfMemoryError: ude af stand til at oprette ny native tråd fejl

2.2. Trådfordelingsmodel

Et operativsystem har typisk to typer tråde - brugertråde (tråde oprettet af en Java-applikation) og kernetråde. Brugertråde understøttes over kernetrådene, og kernetråde styres af operativsystemet.

Mellem dem er der tre almindelige forhold:

  1. Mange-til-en - Mange brugertråde tilknyttes en enkelt kernetråd
  2. En til en - En brugertrådskort til en kernetråd
  3. Mange-for-mange - Mange brugertråde multiplex til et mindre eller lige antal kernetråde

3. Reproduktion af fejlen

Vi kan let genskabe dette problem ved at oprette tråde i en kontinuerlig sløjfe og derefter lade tråde vente:

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

Da vi holder fast på hver tråd i en time, mens vi kontinuerligt opretter nye, når vi hurtigt det maksimale antal tråde fra operativsystemet.

4. Løsninger

En måde at løse denne fejl på er at øge trådgrænsekonfigurationen på OS-niveau.

Dette er dog ikke en ideel løsning, fordi OutOfMemoryError indikerer sandsynligvis en programmeringsfejl. Lad os se på nogle andre måder at løse dette problem på.

4.1. Leveraging Executor Service Framework

Udnyttelse af Java's eksekutortjeneste til trådadministration kan løse dette problem til en vis grad. Standard-eksekverer-servicestellet eller en brugerdefineret eksekveringskonfiguration kan kontrollere oprettelse af tråde.

Vi kan bruge Eksekutører # newFixedThreadPool metode til at indstille det maksimale antal tråde, der kan bruges ad gangen:

ExecutorService executorService = Executors.newFixedThreadPool (5); Runnable runnableTask = () -> {prøv {TimeUnit.HOURS.sleep (1); } fange (InterruptedException e) {// Handle Exception}}; IntStream.rangeClosed (1, 10) .forEach (i -> executorService.submit (runnableTask)); assertThat ((((ThreadPoolExecutor) executorService) .getQueue (). størrelse (), er (equalTo (5)));

I ovenstående eksempel opretter vi først en pool med fast tråd med fem tråde og en kørbar opgave, der får trådene til at vente i en time. Vi sender derefter ti sådanne opgaver til trådpuljen og hævder, at fem opgaver venter i eksekutortjenestekøen.

Da trådpuljen har fem tråde, kan den håndtere maksimalt fem opgaver til enhver tid.

4.2. Indfangning og analyse af tråddumpen

Indfangning og analyse af tråddump er nyttigt til forståelse af trådens status.

Lad os se på en prøve tråddump og se, hvad vi kan lære:

Ovenstående trådbillede er fra Java VisualVM til det eksempel, der blev præsenteret tidligere. Dette øjebliksbillede viser tydeligt den kontinuerlige trådoprettelse.

Når vi først har identificeret, at der er kontinuerlig oprettelse af tråd, kan vi fange tråddumpen i applikationen for at identificere kildekoden, der opretter trådene:

I ovenstående øjebliksbillede kan vi identificere den kode, der er ansvarlig for oprettelsen af ​​tråden. Dette giver nyttig indsigt i at træffe passende foranstaltninger.

5. Konklusion

I denne artikel lærte vi om java.lang.OutOfMemoryError: ude af stand til at oprette ny native tråd fejl, og det så vi det er forårsaget af overdreven oprettelse af tråde i et Java-program.

Vi undersøgte nogle løsninger til at løse og analysere fejlen ved at se på ExecutorService ramme- og tråddumpanalyse som to nyttige foranstaltninger til at tackle dette problem.

Som altid er kildekoden til artiklen tilgængelig på GitHub.