Apa yang Menyebabkan java.lang.OutOfMemoryError: tidak dapat membuat utas asli baru

1. Pengenalan

Dalam tutorial ini, kita akan membincangkan sebab dan kemungkinan penyelesaian java.lang.OutOfMemoryError: tidak dapat membuat ralat benang asli baru .

2. Memahami Masalah

2.1. Punca Masalah

Sebilangan besar aplikasi Java bersifat multithread , terdiri dari beberapa komponen, melakukan tugas-tugas tertentu, dan dilaksanakan dalam berbagai utas. Namun, sistem operasi (OS) yang mendasari mengenakan batas pada jumlah maksimum utas yang dapat dibuat oleh aplikasi Java.

JVM melemparkan tidak dapat membuat ralat benang asli baru apabila JVM meminta OS yang mendasari untuk utas baru, dan OS tidak dapat membuat utas kernel baru yang juga dikenali sebagai utas sistem atau sistem . Urutan peristiwa adalah seperti berikut:

  1. Aplikasi yang berjalan di dalam Java Virtual Machine (JVM) meminta utas baru
  2. Kod asli JVM menghantar permintaan ke OS untuk membuat utas kernel baru
  3. OS cuba membuat utas kernel baru yang memerlukan peruntukan memori
  4. OS menolak peruntukan memori asli kerana kedua-duanya
    • Proses Java yang meminta telah menghabiskan ruang alamat ingatannya
    • OS telah menghabiskan memori maya
  5. Proses Java kemudian mengembalikan java.lang.OutOfMemoryError: tidak dapat membuat ralat benang asli baru

2.2. Model Peruntukan Benang

OS biasanya mempunyai dua jenis utas - utas pengguna (utas yang dibuat oleh aplikasi Java) dan utas kernel . Benang pengguna disokong di atas utas kernel dan utas kernel diuruskan oleh OS.

Di antara mereka, terdapat tiga hubungan yang sama:

  1. Many-To-One - Banyak utas pengguna memetakan ke satu utas kernel
  2. One-To-One - Peta utas satu pengguna ke satu utas kernel
  3. Many-To-Many - Banyak utas pengguna multiplex ke bilangan utas kernel yang lebih kecil atau sama

3. Menghasilkan Ralat

Kami dapat membuat semula masalah ini dengan mudah dengan membuat utas dalam gelung berterusan dan kemudian membuat utas menunggu:

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

Oleh kerana kita memegang setiap utas selama satu jam, sambil terus membuat yang baru, kita akan dengan cepat mencapai bilangan utas maksimum dari OS.

4. Penyelesaian

Salah satu cara untuk mengatasi ralat ini adalah dengan meningkatkan konfigurasi had utas pada peringkat OS.

Walau bagaimanapun, ini bukan penyelesaian yang ideal kerana OutOfMemoryError kemungkinan menunjukkan kesalahan pengaturcaraan. Mari lihat beberapa kaedah lain untuk menyelesaikan masalah ini.

4.1. Memanfaatkan Kerangka Perkhidmatan Pelaksana

Memanfaatkan kerangka perkhidmatan pelaksana Java untuk pentadbiran utas dapat mengatasi masalah ini hingga tahap tertentu. Rangka kerja pelaksana lalai, atau konfigurasi pelaksana khusus, dapat mengawal pembuatan benang.

Kita boleh menggunakan kaedah Executors # newFixedThreadPool untuk menetapkan bilangan utas maksimum yang dapat digunakan pada satu masa:

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)));

Dalam contoh di atas, pertama-tama kita membuat kumpulan utas tetap dengan lima utas dan tugas yang dapat dijalankan yang membuat utas menunggu selama satu jam. Kami kemudian menyerahkan sepuluh tugas tersebut ke kumpulan utas dan menegaskan bahawa lima tugas sedang menunggu dalam barisan perkhidmatan pelaksana.

Oleh kerana kumpulan utas mempunyai lima utas, ia dapat menangani maksimum lima tugas setiap saat.

4.2. Menangkap dan Menganalisis Benang Benang

Menangkap dan menganalisis dump benang berguna untuk memahami status thread.

Mari lihat contoh benang benang dan lihat apa yang dapat kita pelajari:

Cuplikan utas di atas adalah dari Java VisualVM untuk contoh yang dibentangkan sebelumnya. Petikan ini jelas menunjukkan penciptaan benang yang berterusan.

Sebaik sahaja kami mengenal pasti bahawa terdapat penciptaan benang yang berterusan, kami dapat menangkap pembuangan utas aplikasi untuk mengenal pasti kod sumber yang membuat utas:

Dalam gambar di atas, kita dapat mengenal pasti kod yang bertanggungjawab untuk pembuatan utas. Ini memberikan pandangan berguna untuk mengambil langkah-langkah yang sesuai.

5. Kesimpulan

Dalam artikel ini, kami mengetahui tentang java.lang.OutOfMemoryError: tidak dapat membuat ralat benang asli baru , dan kami melihat bahawa ia disebabkan oleh penciptaan thread yang berlebihan dalam aplikasi Java.

Kami meneroka beberapa penyelesaian untuk mengatasi dan menganalisis ralat dengan melihat kerangka ExecutorService dan analisis dump thread sebagai dua langkah yang berguna untuk mengatasi masalah ini.

Seperti biasa, kod sumber untuk artikel tersebut terdapat di GitHub.