Wat veroorzaakt java.lang.OutOfMemoryError: kan geen nieuwe native thread maken

1. Inleiding

In deze zelfstudie bespreken we de oorzaak en mogelijke oplossingen voor het java.lang.OutOfMemoryError: kan geen nieuwe native thread maken fout.

2. Het probleem begrijpen

2.1. Oorzaak van het probleem

De meeste Java-applicaties zijn multithreaded van aard, bestaande uit meerdere componenten, voert specifieke taken uit en wordt uitgevoerd in verschillende threads. Het onderliggende besturingssysteem (OS) legt een limiet op aan het maximale aantal threads die een Java-applicatie kan maken.

De JVM gooit een kan geen nieuwe native thread maken fout wanneer het JVM vraagt ​​het onderliggende besturingssysteem om een ​​nieuwe thread, en het besturingssysteem is niet in staat om nieuwe kernelthreads te maken, ook wel bekend als OS- of systeemthreads. De volgorde van de gebeurtenissen is als volgt:

  1. Een applicatie die binnen de Java Virtual Machine (JVM) draait, vraagt ​​om een ​​nieuwe thread
  2. De native JVM-code stuurt een verzoek naar het besturingssysteem om een ​​nieuwe kerneldraad te maken
  3. Het besturingssysteem probeert een nieuwe kerneldraad te maken die geheugentoewijzing vereist
  4. Het besturingssysteem weigert native geheugentoewijzing omdat ofwel
    • Het verzoekende Het Java-proces heeft zijn geheugenadresruimte verbruikt
    • Het besturingssysteem heeft zijn virtuele geheugen uitgeput
  5. Het Java-proces retourneert vervolgens het java.lang.OutOfMemoryError: kan geen nieuwe native thread maken fout

2.2. Model voor draadtoewijzing

Een besturingssysteem heeft meestal twee soorten threads - gebruikersthreads (threads gemaakt door een Java-applicatie) en kernelthreads. Gebruikersthreads worden ondersteund boven de kernelthreads en de kernelthreads worden beheerd door het besturingssysteem.

Tussen hen zijn er drie gemeenschappelijke relaties:

  1. Veel-op-een - Veel gebruikersthreads zijn toegewezen aan een enkele kerneldraad
  2. Een op een - Eén gebruikersthread-toewijzing aan één kerneldraad
  3. Veel te veel - Veel gebruikers-threads multiplexen naar een kleiner of gelijk aantal kernel-threads

3. Reproductie van de fout

We kunnen dit probleem gemakkelijk opnieuw creëren door threads in een doorlopende lus te maken en de threads vervolgens te laten wachten:

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

Omdat we elke thread een uur vasthouden, terwijl we continu nieuwe maken, zullen we snel het maximale aantal threads van het besturingssysteem bereiken.

4. Oplossingen

Een manier om deze fout te verhelpen, is door de configuratie van de threadlimiet op OS-niveau te verhogen.

Dit is echter geen ideale oplossing omdat de Onvoldoende geheugen fout wijst waarschijnlijk op een programmeerfout. Laten we eens kijken naar enkele andere manieren om dit probleem op te lossen.

4.1. Gebruikmakend van Executor Service Framework

Het gebruik van Java's executor-serviceframework voor threadbeheer kan dit probleem tot op zekere hoogte verhelpen. Het standaard executor-serviceframework of een aangepaste executor-configuratie kan het maken van threads regelen.

We kunnen de Beheerders # newFixedThreadPool methode om het maximale aantal threads in te stellen dat tegelijkertijd kan worden gebruikt:

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

In het bovenstaande voorbeeld maken we eerst een vaste thread-pool met vijf threads en een uitvoerbare taak waardoor de threads een uur wachten. We leggen vervolgens tien van dergelijke taken voor aan de threadpool en stellen dat er vijf taken wachten in de wachtrij van de uitvoerder.

Omdat de threadpool vijf threads heeft, kan deze maximaal vijf taken tegelijk aan.

4.2. Vastleggen en analyseren van de thread-dump

Het vastleggen en analyseren van de threaddump is handig om de status van een thread te begrijpen.

Laten we eens kijken naar een voorbeeld van een thread-dump en kijken wat we kunnen leren:

De bovenstaande thread-momentopname is van Java VisualVM voor het eerder gepresenteerde voorbeeld. Deze momentopname toont duidelijk de continue creatie van threads aan.

Zodra we hebben vastgesteld dat er continu threads worden gemaakt, kunnen we de thread-dump van de toepassing vastleggen om de broncode te identificeren die de threads maakt:

In de bovenstaande momentopname kunnen we de code identificeren die verantwoordelijk is voor het maken van de thread. Dit geeft handig inzicht om passende maatregelen te nemen.

5. Conclusie

In dit artikel hebben we geleerd over de java.lang.OutOfMemoryError: kan geen nieuwe native thread maken fout, en dat hebben we gezien het wordt veroorzaakt door overmatig aanmaken van draden in een Java-applicatie.

We hebben enkele oplossingen onderzocht om de fout aan te pakken en te analyseren door naar het ExecutorService framework en thread dump-analyse als twee nuttige maatregelen om dit probleem aan te pakken.

Zoals altijd is de broncode voor het artikel beschikbaar op GitHub.


$config[zx-auto] not found$config[zx-overlay] not found