Lihatlah dua metode berikut:
public static void foo() {
try {
foo();
} finally {
foo();
}
}
public static void bar() {
bar();
}
Menjalankan bar()
dengan jelas menghasilkan StackOverflowError
, tetapi menjalankan foo()
tidak (program sepertinya berjalan tanpa batas). Mengapa demikian?
java
recursion
stack-overflow
try-finally
arshajii
sumber
sumber
finally
klausa akan merambat ke tingkat berikutnya. Tapi jangan menahan nafas; jumlah langkah yang diambil akan sekitar 2 sampai (kedalaman tumpukan maksimum) dan melempar pengecualian juga tidak murah.bar()
, meskipun.Jawaban:
Itu tidak berjalan selamanya. Setiap stack overflow menyebabkan kode untuk pindah ke blok akhirnya. Masalahnya adalah itu akan memakan waktu yang sangat, sangat lama. Urutan waktu adalah O (2 ^ N) di mana N adalah kedalaman tumpukan maksimum.
Bayangkan kedalaman maksimum adalah 5
Untuk mengerjakan setiap level ke dalam blok terakhir, perlu dua kali lebih panjang dari kedalaman tumpukan yang mencapai 10.000 atau lebih. Jika Anda dapat membuat 10.000.000 panggilan per detik, ini akan memakan waktu 10 ^ 3003 detik atau lebih lama dari usia alam semesta.
sumber
-Xss
, saya mendapatkan kedalaman [150 - 210], jadi 2 ^ n akhirnya menjadi angka digit [47-65]. Tidak akan menunggu selama itu, itu cukup dekat untuk saya.foo
akhirnya tidak berakhir, itu akan menghasilkanStackOverflowError
?Ketika Anda mendapatkan pengecualian dari doa
foo()
di dalamtry
, Anda meneleponfoo()
darifinally
dan mulai berulang lagi. Ketika itu menyebabkan pengecualian lain, Anda akan meneleponfoo()
dari bagian dalam yang lainfinally()
, dan seterusnya hampir tak terhingga .sumber
foo()
dipanggil dari akhirnya setelah BUMN?foo()
doa, dan memohonfoo()
difinally
blok Anda saatfoo()
doa.Coba jalankan kode berikut:
Anda akan menemukan bahwa blok akhirnya dieksekusi sebelum melemparkan Exception ke tingkat di atasnya. (Keluaran:
Ini masuk akal, karena akhirnya dipanggil tepat sebelum keluar dari metode. Ini berarti, bagaimanapun, bahwa setelah Anda mendapatkan yang pertama
StackOverflowError
, itu akan mencoba untuk membuangnya, tetapi akhirnya harus mengeksekusi terlebih dahulu, sehingga berjalanfoo()
lagi, yang mendapat tumpukan stack overflow, dan karena itu berjalan akhirnya lagi. Ini terus terjadi selamanya, sehingga pengecualian tidak pernah benar-benar dicetak.Namun dalam metode bar Anda, segera setelah pengecualian terjadi, itu hanya dilemparkan langsung ke tingkat di atas, dan akan dicetak
sumber
Dalam upaya memberikan bukti yang masuk akal bahwa AKAN ini pada akhirnya akan berakhir, saya menawarkan kode yang agak tidak berarti berikut ini. Catatan: Java BUKAN bahasa saya, oleh imajinasi yang paling jelas. Saya mengajukan hal ini hanya untuk mendukung jawaban Petrus, yang merupakan satu jawaban yang benar untuk pertanyaan itu.
Upaya ini untuk mensimulasikan kondisi dari apa yang terjadi ketika suatu pemanggilan TIDAK dapat terjadi karena itu akan menyebabkan stack overflow. Bagi saya, hal tersulit yang gagal dipahami orang adalah bahwa permohonan itu tidak terjadi ketika itu tidak dapat terjadi.
Keluaran dari tumpukan kecil goo yang tidak berguna ini adalah sebagai berikut, dan pengecualian aktual yang ditangkap mungkin mengejutkan; Oh, dan 32 percobaan panggilan (2 ^ 5), yang sepenuhnya diharapkan:
sumber
Belajar melacak program Anda:
Ini adalah output yang saya lihat:
Seperti yang Anda lihat StackOverFlow dilemparkan pada beberapa layer di atas, jadi Anda bisa melakukan langkah rekursi tambahan hingga Anda menekan pengecualian lain, dan seterusnya. Ini adalah "loop" tanpa batas.
sumber
foo
kedua kalinya, di dalamfinally
blok, ia tidak lagi dalam atry
. Jadi sementara itu akan kembali ke stack dan membuat lebih banyak stack overflow sekali, yang kedua kali itu hanya akan menata kembali kesalahan yang dihasilkan oleh panggilan keduafoo
, alih-alih memperdalam kembali.Program ini sepertinya berjalan selamanya; itu sebenarnya berakhir, tetapi secara eksponensial membutuhkan lebih banyak waktu lebih banyak ruang stack yang Anda miliki. Untuk membuktikan bahwa itu selesai, saya menulis sebuah program yang pertama menghabiskan sebagian besar ruang stack yang tersedia, dan kemudian memanggil
foo
, dan akhirnya menulis jejak apa yang terjadi:Kode:
Anda dapat mencobanya secara online! (Beberapa panggilan mungkin memanggil
foo
lebih atau lebih sedikit daripada yang lain)sumber