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

1. Perkenalan

Dalam tutorial ini, kita akan membahas penyebab dan kemungkinan perbaikan java.lang.OutOfMemoryError: tidak dapat membuat kesalahan utas asli baru .

2. Memahami Masalah

2.1. Penyebab Masalah

Sebagian besar aplikasi Java bersifat multithread , terdiri dari beberapa komponen, melakukan tugas tertentu, dan dijalankan di thread berbeda. Namun, sistem operasi (OS) yang mendasarinya memberlakukan batasan pada jumlah maksimum utas yang dapat dibuat oleh aplikasi Java.

JVM menampilkan error thread baru yang tidak dapat membuat native saat JVM meminta thread baru pada OS yang mendasarinya, dan OS tidak mampu membuat thread kernel baru yang juga dikenal sebagai OS atau thread sistem . Urutan kejadiannya adalah sebagai berikut:

  1. Aplikasi yang berjalan di dalam Java Virtual Machine (JVM) meminta utas baru
  2. Kode asli JVM mengirimkan permintaan ke OS untuk membuat utas kernel baru
  3. OS mencoba membuat utas kernel baru yang memerlukan alokasi memori
  4. OS menolak alokasi memori asli karena keduanya
    • Proses Java yang meminta telah menghabiskan ruang alamat memorinya
    • OS telah menghabiskan memori virtualnya
  5. Proses Java kemudian mengembalikan java.lang.OutOfMemoryError: tidak dapat membuat kesalahan utas asli baru

2.2. Model Alokasi Benang

OS biasanya memiliki dua jenis utas - utas pengguna (utas yang dibuat oleh aplikasi Java) dan utas kernel . Utas pengguna didukung di atas utas kernel dan utas kernel dikelola oleh OS.

Di antara mereka, ada tiga hubungan umum:

  1. Many-To-One - Banyak utas pengguna dipetakan ke utas kernel tunggal
  2. One-To-One - Satu utas pengguna memetakan ke satu utas kernel
  3. Many-To-Many - Banyak pengguna utas multipleks ke jumlah utas kernel yang lebih kecil atau sama

3. Mereproduksi Kesalahan

Kami dapat dengan mudah membuat ulang masalah ini dengan membuat utas dalam putaran berkelanjutan dan kemudian membuat utas menunggu:

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

Karena kami memegang setiap utas selama satu jam, sambil terus membuat utas baru, kami akan dengan cepat mencapai jumlah utas maksimal dari OS.

4. Solusi

Salah satu cara untuk mengatasi kesalahan ini adalah dengan meningkatkan konfigurasi batas utas di tingkat OS.

Namun, ini bukan solusi yang ideal karena OutOfMemoryError kemungkinan besar menunjukkan kesalahan pemrograman. Mari kita lihat beberapa cara lain untuk mengatasi masalah ini.

4.1. Memanfaatkan Kerangka Layanan Pelaksana

Memanfaatkan kerangka layanan eksekutor Java untuk administrasi utas dapat mengatasi masalah ini sampai batas tertentu. Kerangka kerja layanan pelaksana default, atau konfigurasi pelaksana kustom, dapat mengontrol pembuatan utas.

Kita dapat menggunakan metode Executors # newFixedThreadPool untuk mengatur jumlah maksimum utas yang dapat digunakan dalam satu waktu:

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

Pada contoh di atas, pertama-tama kita membuat kumpulan thread tetap dengan lima thread dan tugas yang dapat dijalankan yang membuat thread menunggu selama satu jam. Kami kemudian mengirimkan sepuluh tugas seperti itu ke kumpulan utas dan menegaskan bahwa lima tugas sedang menunggu dalam antrian layanan pelaksana.

Karena kumpulan utas memiliki lima utas, itu dapat menangani maksimal lima tugas kapan saja.

4.2. Menangkap dan Menganalisis Thread Dump

Menangkap dan menganalisis thread dump berguna untuk memahami status thread.

Mari kita lihat contoh thread dump dan lihat apa yang bisa kita pelajari:

Snapshot thread di atas adalah dari Java VisualVM untuk contoh yang disajikan sebelumnya. Snapshot ini dengan jelas menunjukkan pembuatan utas berkelanjutan.

Setelah kami mengidentifikasi bahwa ada pembuatan utas berkelanjutan, kami dapat menangkap utas dump aplikasi untuk mengidentifikasi kode sumber yang membuat utas:

Pada snapshot di atas, kita dapat mengidentifikasi kode yang bertanggung jawab atas pembuatan thread. Ini memberikan wawasan yang berguna untuk mengambil tindakan yang tepat.

5. Kesimpulan

Dalam artikel ini, kita belajar tentang java.lang.OutOfMemoryError: tidak dapat membuat kesalahan utas asli baru , dan kami melihat bahwa itu disebabkan oleh pembuatan utas yang berlebihan dalam aplikasi Java.

Kami mempelajari beberapa solusi untuk mengatasi dan menganalisis kesalahan dengan melihat kerangka kerja ExecutorService dan analisis dump thread sebagai dua tindakan yang berguna untuk mengatasi masalah ini.

Seperti biasa, kode sumber artikel tersedia di GitHub.