Saya terkejut bagaimana mungkin untuk melanjutkan eksekusi bahkan setelah StackOverflowError
terjadi di Java.
Saya tahu itu StackOverflowError
adalah sub kelas dari kelas Error. Kelas Error dideklarasikan sebagai "subclass dari Throwable yang menunjukkan masalah serius yang seharusnya tidak ditangkap oleh aplikasi yang wajar."
Ini terdengar lebih seperti rekomendasi daripada aturan, subtending bahwa menangkap Error seperti StackOverflowError sebenarnya diizinkan dan terserah pada alasan programmer untuk tidak melakukannya. Dan lihat, saya menguji kode ini dan itu berakhir secara normal.
public class Test
{
public static void main(String[] args)
{
try {
foo();
} catch (StackOverflowError e) {
bar();
}
System.out.println("normal termination");
}
private static void foo() {
System.out.println("foo");
foo();
}
private static void bar() {
System.out.println("bar");
}
}
Bagaimana ini bisa terjadi? Saya pikir pada saat StackOverflowError dilempar, tumpukan seharusnya sudah penuh sehingga tidak ada ruang untuk memanggil fungsi lain. Apakah blok penanganan kesalahan berjalan di tumpukan yang berbeda, atau apa yang terjadi di sini?
sumber
Jawaban:
Saat tumpukan meluap dan
StackOverflowError
terlempar, penanganan pengecualian biasa melepaskan tumpukan. Melepas tumpukan berarti:... sampai pengecualiannya tertangkap. Ini normal (pada kenyataannya, perlu) dan terlepas dari pengecualian mana yang dilemparkan dan mengapa. Karena Anda menangkap pengecualian di luar panggilan pertama ke
foo()
, ribuanfoo
bingkai tumpukan yang mengisi tumpukan semuanya telah dibatalkan dan sebagian besar tumpukan bebas untuk digunakan kembali.sumber
foo
diakhiri dengan keadaan tidak terdefinisi, jadi setiap benda yang mungkin disentuhnya harus dianggap rusak. Karena Anda tidak tahu di mana fungsi stack overflow terjadi, hanya saja itu harus merupakan turunan daritry
blok yang menangkapnya, objek apa pun yang dapat dimodifikasi dengan metode apa pun yang dapat dijangkau dari sana sekarang dicurigai. Biasanya tidak ada gunanya mencari tahu apa yang terjadi dan mencoba memperbaikinya.Error
s tidak dapat diantisipasi bahkan saat menulis kode aman pengecualian.Error
s. OP hanya menanyakan tentangStackOverflowError
, dan itu menanyakan hal tertentu dari penanganan kesalahan ini : bagaimana metode panggilan tidak gagal ketika kesalahan ini tertangkap.Saat StackOverflowError dilemparkan, tumpukan sudah penuh. Namun, saat tertangkap , semua
foo
panggilan tersebut telah dikeluarkan dari tumpukan.bar
dapat berjalan normal karena tumpukan tidak lagi dipenuhi denganfoo
s. (Perhatikan bahwa menurut saya JLS tidak menjamin Anda dapat memulihkan dari stack overflow seperti ini.)sumber
Saat StackOverFlow terjadi, JVM akan turun ke tangkapan, membebaskan tumpukan.
Dalam contoh Anda, ia mendapatkan semua tumpukan foo.
sumber
Karena tumpukan tidak benar-benar meluap. Nama yang lebih baik mungkin adalah AttemptToOverflowStack. Pada dasarnya apa yang dimaksud adalah bahwa upaya terakhir untuk menyesuaikan bingkai tumpukan keliru karena tidak ada cukup ruang kosong yang tersisa di tumpukan. Tumpukan sebenarnya bisa memiliki banyak ruang tersisa, hanya saja tidak cukup ruang. Jadi, operasi apa pun akan bergantung pada panggilan yang berhasil (biasanya pemanggilan metode), tidak pernah dieksekusi dan semua yang tersisa adalah program untuk menangani fakta itu. Artinya, ini tidak berbeda dengan pengecualian lainnya. Sebenarnya, Anda bisa menangkap pengecualian dalam fungsi yang melakukan panggilan tersebut.
sumber
Seperti yang telah dijawab , dimungkinkan untuk mengeksekusi kode, dan khususnya untuk memanggil fungsi, setelah menangkap a
StackOverflowError
karena prosedur penanganan pengecualian normal dari JVM melepaskan tumpukan antara titikthrow
dancatch
titik, membebaskan ruang tumpukan untuk Anda gunakan. Dan percobaan Anda menegaskan bahwa itu masalahnya.Namun, itu tidak sama dengan mengatakan bahwa secara umum dimungkinkan untuk pulih dari a
StackOverflowError
.A
StackOverflowError
IS-AVirtualMachineError
, yaitu IS-ANError
. Seperti yang Anda tunjukkan, Java memberikan beberapa saran samar untukError
:dan Anda, cukup, menyimpulkan bahwa harus terdengar seperti penangkapan
Error
mungkin OK dalam beberapa keadaan. Perhatikan bahwa melakukan satu eksperimen tidak menunjukkan bahwa ada sesuatu yang secara umum aman untuk dilakukan. Hanya aturan bahasa Java dan spesifikasi kelas yang Anda gunakan yang dapat melakukannya. AVirtualMachineError
adalah kelas pengecualian khusus, karena Spesifikasi Bahasa Java dan Spesifikasi Mesin Virtual Java memberikan informasi tentang semantik pengecualian ini. Secara khusus, yang terakhir mengatakan :...
Masalah krusialnya adalah Anda "tidak bisa memprediksi" di mana atau kapan surat
StackOverflowError
wasiat akan dilemparkan. Tidak ada jaminan di mana tidak akan dilempar. Anda tidak dapat mengandalkannya untuk dilemparkan saat masuk ke metode, misalnya. Itu bisa dilemparkan pada suatu titik dalam suatu metode.Ketidakpastian ini berpotensi menjadi bencana. Karena dapat dilemparkan di dalam metode, ia dapat dilemparkan sebagian melalui urutan operasi yang dianggap kelas sebagai satu operasi "atom", meninggalkan objek dalam keadaan yang dimodifikasi sebagian, tidak konsisten. Dengan objek dalam keadaan tidak konsisten, upaya apa pun untuk menggunakan objek tersebut dapat mengakibatkan perilaku yang salah. Dalam semua kasus praktis, Anda tidak dapat mengetahui objek mana yang berada dalam kondisi tidak konsisten, jadi Anda harus berasumsi bahwa tidak ada objek yang dapat dipercaya. Setiap operasi pemulihan atau upaya untuk melanjutkan setelah pengecualian tertangkap karena itu bisa memiliki perilaku yang keliru. Oleh karena itu, satu-satunya hal yang aman untuk dilakukan adalah tidak menangkap a
StackOverflowError
, melainkan untuk mengizinkan program dihentikan. (Dalam praktiknya Anda mungkin mencoba melakukan beberapa pencatatan kesalahan untuk membantu pemecahan masalah, tetapi Anda tidak dapat mengandalkan pencatatan itu beroperasi dengan benar). Artinya, Anda tidak dapat memulihkan secara andal dari aStackOverflowError
.sumber