Mengapa mungkin untuk memulihkan dari StackOverflowError?

100

Saya terkejut bagaimana mungkin untuk melanjutkan eksekusi bahkan setelah StackOverflowErrorterjadi di Java.

Saya tahu itu StackOverflowErroradalah 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?

pengguna3370796
sumber
57
Saya membuat kesalahan di StackOverflow sepanjang waktu. Tidak menghentikan saya untuk kembali.
10
Yo dawg ... Saya dengar Anda menyukai stack overflows, jadi kami menempatkan stack overflow di stackoverflow.com Anda!
Pierre Henry
Karena arsitektur modern menggunakan Frame Pointers untuk memfasilitasi tumpukan tumpukan, bahkan yang sebagian. Selama kode + konteks untuk melakukan itu tidak harus dialokasikan secara dinamis dari tumpukan, seharusnya tidak ada masalah.
RBarryYoung

Jawaban:

119

Saat tumpukan meluap dan StackOverflowErrorterlempar, penanganan pengecualian biasa melepaskan tumpukan. Melepas tumpukan berarti:

  • batalkan eksekusi fungsi yang saat ini aktif
  • hapus bingkai tumpukannya, lanjutkan dengan fungsi panggilan
  • batalkan eksekusi pemanggil
  • hapus bingkai tumpukannya, lanjutkan dengan fungsi panggilan
  • dan seterusnya...

... 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(), ribuan foobingkai tumpukan yang mengisi tumpukan semuanya telah dibatalkan dan sebagian besar tumpukan bebas untuk digunakan kembali.


sumber
1
@fge Jangan ragu untuk mengedit, saya mempertimbangkan pemisah paragraf tetapi tidak dapat menemukan tempat yang terlihat bagus.
1
Anda dapat menggunakan poin-poin ... Saya enggan mengedit postingan orang lain;)
fge
1
Intinya adalah yang terdalam foodiakhiri 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 dari tryblok 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.
Simon Richter
2
@delnan, menurut saya jawabannya tidak lengkap tanpa menjelaskan secara detail mengapa ini adalah ide yang buruk. Perbedaan dari eksepsi yang ditampilkan secara eksplisit adalah bahwa Errors tidak dapat diantisipasi bahkan saat menulis kode aman pengecualian.
Simon Richter
1
@SimonRichter Tidak, pertanyaannya cukup spesifik. Ini bukan tentang menangani Errors. OP hanya menanyakan tentang StackOverflowError, dan itu menanyakan hal tertentu dari penanganan kesalahan ini : bagaimana metode panggilan tidak gagal ketika kesalahan ini tertangkap.
Bakuriu
23

Saat StackOverflowError dilemparkan, tumpukan sudah penuh. Namun, saat tertangkap , semua foopanggilan tersebut telah dikeluarkan dari tumpukan. bardapat berjalan normal karena tumpukan tidak lagi dipenuhi dengan foos. (Perhatikan bahwa menurut saya JLS tidak menjamin Anda dapat memulihkan dari stack overflow seperti ini.)

user2357112 mendukung Monica
sumber
12

Saat StackOverFlow terjadi, JVM akan turun ke tangkapan, membebaskan tumpukan.

Dalam contoh Anda, ia mendapatkan semua tumpukan foo.

Nicolas Defranoux
sumber
8

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.

jmoreno
sumber
1
Berhati-hatilah jika Anda melakukan ini karena penangan pengecualian Anda tidak memerlukan lebih banyak ruang tumpukan daripada yang tersedia!
Vince
2

Seperti yang telah dijawab , dimungkinkan untuk mengeksekusi kode, dan khususnya untuk memanggil fungsi, setelah menangkap a StackOverflowErrorkarena prosedur penanganan pengecualian normal dari JVM melepaskan tumpukan antara titik throwdan catchtitik, 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 StackOverflowErrorIS-A VirtualMachineError, yaitu IS-AN Error. Seperti yang Anda tunjukkan, Java memberikan beberapa saran samar untuk Error:

menunjukkan masalah serius yang seharusnya tidak ditangani oleh aplikasi yang wajar

dan Anda, cukup, menyimpulkan bahwa harus terdengar seperti penangkapan Errormungkin 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. A VirtualMachineErroradalah kelas pengecualian khusus, karena Spesifikasi Bahasa Java dan Spesifikasi Mesin Virtual Java memberikan informasi tentang semantik pengecualian ini. Secara khusus, yang terakhir mengatakan :

Implementasi Java Virtual Machine melontarkan objek yang merupakan turunan dari subkelas kelas VirtualMethodErrorsaat kesalahan internal atau batasan sumber daya mencegahnya untuk mengimplementasikan semantik yang dijelaskan dalam bab ini. Spesifikasi ini tidak dapat memprediksi di mana kesalahan internal atau keterbatasan sumber daya dapat ditemukan dan tidak mewajibkan secara tepat kapan kesalahan tersebut dapat dilaporkan. Dengan demikian, salah satu VirtualMethodErrorsubclass yang ditentukan di bawah ini dapat dilemparkan kapan saja selama pengoperasian Mesin Virtual Java:

...

  • StackOverflowError: Implementasi Java Virtual Machine telah kehabisan ruang tumpukan untuk sebuah utas, biasanya karena utas melakukan pemanggilan rekursif dalam jumlah tak terbatas sebagai akibat dari kesalahan dalam program yang sedang dijalankan.

Masalah krusialnya adalah Anda "tidak bisa memprediksi" di mana atau kapan surat StackOverflowErrorwasiat 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 aStackOverflowError, 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 .

Raedwald
sumber