Bagaimana JVM menangani pengecualian yang dilemparkan oleh metode utama?

10

Saya memahami pengecualian, melempar mereka, menangani mereka, dan menyebarkannya ke metode yang lebih rendah di tumpukan panggilan (yaitu throws).

Yang tidak saya mengerti adalah ini:

public static void main(String[] args) throws Exception {
    ...
}

Sekarang, saya berasumsi bahwa dalam kasus yang mainmelempar Exception, JVM menanganinya (benar?). Jika itu masalahnya, maka pertanyaan saya adalah:

Bagaimana cara JVM menangani pengecualian main? Apa fungsinya?

Aviv Cohn
sumber

Jawaban:

19

Anda mungkin berpikir bahwa public static void mainmetode di Java atau mainfungsi di C adalah titik masuk nyata dari program Anda - tetapi tidak. Semua bahasa tingkat tinggi (termasuk C) memiliki runtime bahasa yang menginisialisasi program, dan kemudian mentransfer aliran kontrol ke titik masuk. Dalam kasus Java, inisialisasi akan mencakup:

  • mengatur JVM
  • memuat kelas yang diperlukan
  • menjalankan blok penginisialisasi statis. Ini dapat mengeksekusi kode yang ditentukan pengguna sebelum maindipanggil. Blok ini seharusnya tidak memberikan pengecualian.

Ada berbagai cara untuk menerapkan penanganan pengecualian, tetapi untuk tujuan pertanyaan ini, semuanya dapat dilihat sebagai kotak hitam. Namun yang penting adalah bahwa runtime bahasa harus selalu memberikan handler pengecualian terluar yang menangkap semua pengecualian yang tidak ditangkap oleh kode pengguna. Handler pengecualian ini biasanya akan mencetak jejak tumpukan, mematikan program secara teratur, dan keluar dengan kode kesalahan. Mematikan program dengan benar termasuk menghancurkan grafik objek, memanggil finalizer, dan membebaskan sumber daya seperti memori, file menangani, atau koneksi jaringan.

Untuk tujuan ilustrasi, Anda dapat memetakan runtime yang membungkus semua kode dalam try-catch raksasa yang terlihat seperti

try {
    loadClasses();
    runInitializers();
    main(argv);
    System.exit(0);
} catch (Throwable e) {
    e.printStackTrace();
    System.exit(-1);
}

kecuali bahwa bahasa tidak perlu untuk benar-benar mengeksekusi kode seperti ini. Semantik yang sama dapat diimplementasikan dalam kode untuk throw(atau setara) yang mencari penangan pengecualian pertama yang berlaku.

amon
sumber
9

Semua kode Java berjalan dalam konteks utas . JavaDoc yang ditautkan menjelaskan kriteria penanganan dan keluar kesalahan, tetapi di sini adalah intinya:

  • JVM memutar dirinya sendiri dan menyiapkan lingkungan eksekusi.
  • JVM membuat utas yang akan menjalankan main()metode menggunakan parameter baris perintah apa pun yang berlaku.
  • JVM menetapkan handler pengecualian tanpa tertangkap standar yang mencetak pengecualian untuk kesalahan standar dan berakhir.
  • JVM mengeksekusi utas.

Dalam kasus pengecualian tanpa tertangkap, program secara efektif mati per item ketiga di atas. Perilaku ini selanjutnya ditentukan dalam Spesifikasi Bahasa Jawa, Bagian 11.3


informasi tambahan

Yang lain telah menyebutkan blok statis dan bagaimana mereka mengeksekusi sebelumnya main(). Namun, ini membutuhkan penjelasan yang lebih banyak untuk dapat dipahami dengan benar.

Saat memuat kelas, pemuat kelas harus menginisialisasi semua static finalnegara dan menjalankan semua staticblok sebelum kelas dapat digunakan, untuk memasukkan instance instance kelas (selain: membuat kelas Java di mana konstanta kelas diinisialisasi dalam blok statis setelah membuat turunan dari kelas, dan konstruktor mereferensikan konstanta. Boom!). Namun, ini semua terjadi dalam logika loader kelas sebelum kode apa pun dapat merujuk kelas . Selanjutnya, kelas dimuat dalam utas apa pun yang merujuk kelas.

Apa artinya ini adalah jika kelas yang berisi main()referensi kelas lain (misalnya konstanta kelas) maka kelas itu harus dimuat sebelum main()dijalankan untuk menyertakan blok statisnya. Kalau tidak, blok statis dieksekusi seperti di atas. Jika kelas gagal memuat, maka kelas yang berisi main()juga akan gagal memuat dan program akan berakhir.

FYI lain: blok statis bisa melempar. Errorsdilempar apa adanya. Exceptionsdilarang (kesalahan waktu kompilasi). RuntimeExceptionsterbungkus dalam ExceptionInInitializerError . Ini ditangani per handler pengecualian tanpa tertangkap, yang biasanya akan mematikan utas atau aplikasi (utas utama) kecuali Anda dengan hati-hati membungkus referensi kelas (dan memuat) dalam a try- catch.

Benni
sumber