Dokumentasi untuk java.lang.Error
kata:
Error adalah subclass dari Throwable yang menunjukkan masalah serius yang tidak boleh ditangkap oleh aplikasi yang wajar
Tapi sebagai java.lang.Error
subclass dari java.lang.Throwable
, saya bisa menangkap jenis Throwable ini.
Saya mengerti mengapa menangkap pengecualian semacam ini bukanlah ide yang baik. Sejauh yang saya mengerti, jika kita memutuskan untuk menangkapnya, penangan tangkapan seharusnya tidak mengalokasikan memori apapun dengan sendirinya. Kalau tidak OutOfMemoryError
akan terlempar lagi.
Jadi, pertanyaan saya adalah:
- Apakah ada skenario dunia nyata saat menangkap
java.lang.OutOfMemoryError
mungkin ide yang bagus? - Jika kita memutuskan untuk menangkap
java.lang.OutOfMemoryError
, bagaimana kita bisa memastikan penangan tangkapan tidak mengalokasikan memori apa pun dengan sendirinya (alat atau praktik terbaik apa pun)?
java
try-catch
out-of-memory
Denis Bazhenov
sumber
sumber
Jawaban:
Saya setuju dan tidak setuju dengan sebagian besar tanggapan di sini.
Ada sejumlah skenario di mana Anda mungkin ingin menangkap
OutOfMemoryError
dan menurut pengalaman saya (di Windows dan Solaris JVM), hanya sangat jarangOutOfMemoryError
lonceng kematian untuk JVM.Hanya ada satu alasan bagus untuk menangkap
OutOfMemoryError
dan itu adalah untuk menutup dengan anggun, melepaskan sumber daya dengan bersih dan mencatat alasan kegagalan sebaik mungkin (jika masih mungkin untuk melakukannya).Secara umum, file
OutOfMemoryError
terjadi karena alokasi memori blok yang tidak dapat dipenuhi dengan sumber daya heap yang tersisa.Ketika
Error
dilempar, heap berisi objek yang dialokasikan dalam jumlah yang sama seperti sebelumnya alokasi yang gagal dan sekarang adalah waktunya untuk melepaskan referensi ke objek run-time untuk membebaskan lebih banyak memori yang mungkin diperlukan untuk pembersihan. Dalam kasus ini, bahkan mungkin untuk melanjutkan tetapi itu pasti ide yang buruk karena Anda tidak pernah bisa 100% yakin bahwa JVM dalam keadaan dapat diperbaiki.Demonstrasi yang
OutOfMemoryError
tidak berarti bahwa JVM kehabisan memori di blok catch:Output dari kode ini:
Jika menjalankan sesuatu yang penting, saya biasanya menangkap
Error
, mencatatnya ke syserr, kemudian mencatatnya menggunakan kerangka kerja logging pilihan saya, kemudian melanjutkan untuk melepaskan sumber daya dan menutup dengan cara yang bersih. Hal terburuk apa yang bisa terjadi? JVM sedang sekarat (atau sudah mati) dan dengan menangkapnyaError
setidaknya ada kemungkinan pembersihan.Peringatannya adalah Anda harus menargetkan penangkapan jenis kesalahan ini hanya di tempat yang memungkinkan pembersihan. Jangan menyelimuti di
catch(Throwable t) {}
mana-mana atau omong kosong seperti itu.sumber
OutOfMemoryError
mungkin telah dilempar dari titik yang telah menempatkan program Anda dalam keadaan tidak konsisten, karena dapat dilempar kapan saja. Lihat stackoverflow.com/questions/8728866/…Anda dapat memulihkannya:
Tapi apakah itu masuk akal? Apa lagi yang ingin Anda lakukan? Mengapa Anda awalnya mengalokasikan memori sebanyak itu? Apakah lebih sedikit memori juga oke? Mengapa Anda belum memanfaatkannya? Atau jika itu tidak memungkinkan, mengapa tidak memberikan lebih banyak memori pada JVM dari awal?
Kembali ke pertanyaan Anda:
Tidak ada yang terlintas dalam pikiran.
Tergantung pada apa yang menyebabkan OOME. Jika diumumkan di luar
try
blok dan terjadi selangkah demi selangkah, maka peluang Anda kecil. Anda mungkin ingin memesan beberapa ruang memori sebelumnya:dan kemudian setel ke nol selama OOME:
Tentu saja ini semua tidak masuk akal;) Berikan saja memori yang cukup JVM sesuai kebutuhan aplikasi Anda. Jalankan profiler jika perlu.
sumber
null
?Secara umum, mencoba menangkap dan memulihkan dari OOM adalah ide yang buruk.
OOME juga bisa saja terlempar ke utas lain, termasuk utas yang bahkan tidak diketahui aplikasi Anda. Utas seperti itu sekarang akan mati, dan apa pun yang menunggu pemberitahuan bisa macet selamanya. Singkatnya, aplikasi Anda bisa saja rusak.
Meskipun Anda berhasil memulihkan, JVM Anda mungkin masih menderita kelaparan heap dan aplikasi Anda akan bekerja sangat buruk sebagai hasilnya.
Hal terbaik yang dapat dilakukan dengan OOME adalah membiarkan JVM mati.
(Ini mengasumsikan bahwa JVM melakukannya mati. Misalnya OOMS pada servlet benang Tomcat tidak membunuh JVM, dan lead ini ke Tomcat pergi ke negara katatonik mana itu tidak akan merespon setiap permintaan ... bahkan tidak meminta untuk mengulang kembali.)
EDIT
Saya tidak mengatakan bahwa menangkap OOM sama sekali adalah ide yang buruk. Masalah muncul ketika Anda kemudian mencoba untuk memulihkan OOME, baik secara sengaja atau karena pengawasan. Setiap kali Anda menemukan OOM (secara langsung, atau sebagai subtipe Error atau Throwable), Anda harus mengembalikannya, atau mengatur agar aplikasi / JVM keluar.
Selain: Ini menyarankan bahwa untuk ketahanan maksimum dalam menghadapi OOM, aplikasi harus menggunakan Thread.setDefaultUncaughtExceptionHandler () untuk menyetel penangan yang akan menyebabkan aplikasi keluar jika terjadi OOME, apa pun utas tempat OOME dilemparkan. Saya tertarik dengan opini tentang ini ...
Satu-satunya skenario lain adalah ketika Anda mengetahui dengan pasti bahwa OOM tidak mengakibatkan kerusakan tambahan; yaitu Anda tahu:
Ada beberapa aplikasi yang memungkinkan untuk mengetahui hal-hal ini, tetapi untuk sebagian besar aplikasi Anda tidak dapat mengetahui dengan pasti bahwa kelanjutan setelah OOME aman. Bahkan jika secara empiris "berhasil" saat Anda mencobanya.
(Masalahnya adalah bahwa diperlukan bukti resmi untuk menunjukkan bahwa konsekuensi dari OOME yang "diantisipasi" aman, dan bahwa OOME yang "tak terduga" tidak dapat terjadi dalam kendali percobaan / tangkap OOME.)
sumber
Error
.Ya, ada skenario dunia nyata. Ini milik saya: Saya perlu memproses kumpulan data dari sangat banyak item di cluster dengan memori terbatas per node. Instance JVM tertentu melewati banyak item satu demi satu, tetapi beberapa item terlalu besar untuk diproses di cluster: Saya dapat menangkap
OutOfMemoryError
dan mencatat item mana yang terlalu besar. Nanti, saya dapat menjalankan kembali item besar di komputer dengan lebih banyak RAM.(Karena ini adalah alokasi multi-gigabyte tunggal dari sebuah array yang gagal, JVM masih baik-baik saja setelah menemukan kesalahan dan ada cukup memori untuk memproses item lainnya.)
sumber
byte[] bytes = new byte[length]
? Mengapa tidak memeriksasize
di poin sebelumnya?size
akan baik-baik saja dengan lebih banyak memori. Saya pergi melalui pengecualian karena dalam banyak kasus semuanya akan baik-baik saja.Pasti ada skenario di mana menangkap OOME masuk akal. IDEA menangkap mereka dan memunculkan dialog untuk memungkinkan Anda mengubah pengaturan memori startup (dan kemudian keluar setelah Anda selesai). Server aplikasi mungkin menangkap dan melaporkannya. Kunci untuk melakukan ini adalah melakukannya pada tingkat tinggi pada pengiriman sehingga Anda memiliki peluang yang masuk akal untuk memiliki banyak sumber daya yang dibebaskan pada titik di mana Anda menangkap pengecualian.
Selain skenario IDEA di atas, secara umum penangkapan harus Throwable, bukan hanya OOM secara spesifik, dan harus dilakukan dalam konteks di mana setidaknya thread akan segera dihentikan.
Tentu saja seringkali memori kelaparan dan situasinya tidak dapat dipulihkan, tetapi ada cara yang masuk akal.
sumber
Saya menemukan pertanyaan ini karena saya bertanya-tanya apakah menangkap OutOfMemoryError dalam kasus saya adalah ide yang bagus. Saya menjawab di sini sebagian untuk menunjukkan contoh lain ketika menangkap kesalahan ini dapat masuk akal bagi seseorang (yaitu saya) dan sebagian untuk mengetahui apakah itu ide yang baik dalam kasus saya (dengan saya menjadi pengembang junior uber saya tidak akan pernah bisa terlalu yakin tentang satu baris kode yang saya tulis).
Bagaimanapun, saya sedang mengerjakan aplikasi Android yang dapat dijalankan pada perangkat berbeda dengan ukuran memori berbeda. Bagian berbahaya adalah mendekode bitmap dari file dan menampilkannya dalam instance ImageView. Saya tidak ingin membatasi perangkat yang lebih kuat dalam hal ukuran bitmap yang didekodekan, juga tidak dapat memastikan bahwa aplikasi tidak akan dijalankan pada perangkat kuno yang tidak pernah saya temui dengan memori yang sangat rendah. Karenanya saya melakukan ini:
Dengan cara ini saya berhasil menyediakan perangkat yang lebih dan kurang kuat sesuai dengan mereka, atau lebih tepatnya kebutuhan dan harapan penggunanya.
sumber
Ya, pertanyaan sebenarnya adalah "apa yang akan Anda lakukan di penangan pengecualian?" Untuk hampir semua hal yang berguna, Anda akan mengalokasikan lebih banyak memori. Jika Anda ingin melakukan beberapa pekerjaan diagnostik saat OutOfMemoryError terjadi, Anda dapat menggunakan
-XX:OnOutOfMemoryError=<cmd>
hook yang disediakan oleh VM HotSpot. Ini akan menjalankan perintah Anda saat OutOfMemoryError terjadi, dan Anda dapat melakukan sesuatu yang berguna di luar heap Java. Anda benar-benar ingin agar aplikasi tidak kehabisan memori sejak awal, jadi mencari tahu mengapa hal itu terjadi adalah langkah pertama. Kemudian Anda dapat meningkatkan ukuran tumpukan MaxPermSize yang sesuai. Berikut beberapa pengait HotSpot berguna lainnya:Lihat daftar lengkapnya di sini
sumber
OutOfMemeoryError
dapat dilemparkan kapan saja dalam program Anda (tidak hanya darinew
pernyataan) program Anda akan berada dalam status tidak ditentukan saat Anda menangkap pengecualian.Saya memiliki aplikasi yang perlu pulih dari kegagalan OutOfMemoryError, dan dalam program single-threaded itu selalu berfungsi, tetapi terkadang tidak dalam program multi-threaded. Aplikasi tersebut adalah alat pengujian Java otomatis yang menjalankan urutan pengujian yang dihasilkan ke kedalaman semaksimal mungkin pada kelas pengujian. Sekarang, UI harus stabil, tetapi mesin uji dapat kehabisan memori saat mengembangkan pohon kasus pengujian. Saya menangani ini dengan jenis idiom kode berikut di mesin uji:
Ini berfungsi setiap saat dalam aplikasi single-threaded. Tapi saya baru-baru ini menempatkan mesin uji saya ke thread pekerja terpisah dari UI. Sekarang, kehabisan memori dapat terjadi secara sewenang-wenang di salah satu utas, dan tidak jelas bagi saya cara menangkapnya.
Misalnya, saya mengalami OOME terjadi saat bingkai GIF animasi di UI saya diputar ulang oleh utas berpemilik yang dibuat di belakang layar oleh kelas Swing yang berada di luar kendali saya. Saya berpikir bahwa saya telah mengalokasikan semua sumber daya yang dibutuhkan sebelumnya, tetapi yang jelas animator mengalokasikan memori setiap kali mengambil gambar berikutnya. Jika ada yang punya ide tentang bagaimana menangani OOME yang diangkat di utas mana pun , saya akan senang mendengarnya.
sumber
OOME dapat ditangkap tetapi secara umum tidak akan berguna, tergantung pada apakah JVM dapat mengumpulkan sampah untuk mengumpulkan beberapa objek saat tangkapan tercapai, dan berapa banyak memori heap yang tersisa saat itu.
Contoh: di JVM saya, program ini berjalan sampai selesai:
Namun, hanya menambahkan satu baris pada tangkapan akan menunjukkan kepada Anda apa yang saya bicarakan:
Program pertama berjalan dengan baik karena ketika tangkapan tercapai, JVM mendeteksi bahwa daftar tersebut tidak akan digunakan lagi (Deteksi ini juga dapat berupa pengoptimalan yang dilakukan pada waktu kompilasi). Jadi ketika kami mencapai pernyataan cetak, memori heap telah dibebaskan hampir seluruhnya, jadi kami sekarang memiliki margin manuver yang lebar untuk melanjutkan. Ini kasus terbaik.
Namun, jika kode diatur seperti daftar
ll
yang digunakan setelah OOME ditangkap, JVM tidak dapat mengumpulkannya. Ini terjadi di cuplikan kedua. OOME, yang dipicu oleh pembuatan Long baru, ditangkap, tetapi segera kami membuat Objek baru (String diSystem.out,println
baris), dan tumpukan hampir penuh, jadi OOME baru dilemparkan. Ini adalah skenario kasus terburuk: kami mencoba membuat objek baru, kami gagal, kami menangkap OOME, ya, tetapi sekarang instruksi pertama yang membutuhkan memori heap baru (misalnya: membuat objek baru) akan mengeluarkan OOME baru. Pikirkan tentang itu, apa lagi yang bisa kita lakukan pada saat ini dengan sedikit memori yang tersisa ?. Mungkin baru keluar. Karenanya yang tidak berguna.Di antara alasan JVM tidak mengumpulkan sumber daya, satu hal yang sangat menakutkan: sumber daya bersama dengan utas lain juga memanfaatkannya. Siapa pun yang memiliki otak dapat melihat betapa berbahayanya menangkap OOME jika dimasukkan ke aplikasi non-eksperimental dalam bentuk apa pun.
Saya menggunakan Windows x86 32bits JVM (JRE6). Memori default untuk setiap aplikasi Java adalah 64MB.
sumber
ll=null
di dalam blok tangkapan?Satu-satunya alasan saya dapat memikirkan mengapa menangkap kesalahan OOM mungkin karena Anda memiliki beberapa struktur data besar yang tidak Anda gunakan lagi, dan dapat disetel ke nol dan membebaskan beberapa memori. Tapi (1) itu berarti Anda membuang-buang memori, dan Anda harus memperbaiki kode Anda daripada hanya tertatih-tatih setelah OOME, dan (2) bahkan jika Anda menangkapnya, apa yang akan Anda lakukan? OOM dapat terjadi kapan saja, berpotensi meninggalkan semuanya setengah jadi.
sumber
Untuk pertanyaan 2, saya sudah melihat solusi yang saya sarankan, oleh BalusC.
Saya rasa saya baru saja menemukan contoh yang bagus. Saat aplikasi awt mengirimkan pesan, OutOfMemoryError yang tidak ditambal ditampilkan di stderr dan pemrosesan pesan saat ini dihentikan. Tapi aplikasinya tetap berjalan! Pengguna mungkin masih mengeluarkan perintah lain tanpa menyadari masalah serius yang terjadi di balik layar. Terutama ketika dia tidak bisa atau tidak mengamati kesalahan standar. Jadi menangkap pengecualian dan menyediakan (atau setidaknya menyarankan) aplikasi restart adalah sesuatu yang diinginkan.
sumber
Saya hanya memiliki skenario di mana menangkap OutOfMemoryError tampaknya masuk akal dan tampaknya berhasil.
Skenario: di Aplikasi Android, saya ingin menampilkan beberapa bitmap dalam resolusi setinggi mungkin, dan saya ingin dapat memperbesarnya dengan lancar.
Karena pembesaran yang lancar, saya ingin memiliki bitmap di memori. Namun, Android memiliki keterbatasan dalam memori yang bergantung pada perangkat dan yang sulit dikendalikan.
Dalam situasi ini, mungkin ada OutOfMemoryError saat membaca bitmap. Di sini, akan membantu jika saya menangkapnya dan kemudian melanjutkan dengan resolusi yang lebih rendah.
sumber
OutOfMemory
tidak terjadi karena perbaikan yang tidak terkait). Namun, meskipun Anda menangkapnya, itu mungkin masih merusak beberapa kode penting: jika Anda memiliki beberapa utas, alokasi memori dapat gagal di salah satu utas. Jadi, tergantung pada aplikasi Anda, masih ada 10--90% kemungkinannya rusak permanen.EDIT: Saya sarankan Anda mencobanya. Katakanlah, tulis program yang secara rekursif memanggil fungsi yang mengalokasikan lebih banyak memori secara progresif. Tangkap
OutOfMemoryError
dan lihat apakah Anda dapat melanjutkan dari titik itu. Menurut pengalaman saya, Anda akan bisa, meskipun dalam kasus saya itu terjadi di bawah server WebLogic, jadi mungkin ada beberapa ilmu hitam yang terlibat.sumber
Anda dapat menangkap apa saja di bawah Throwable, secara umum Anda hanya boleh menangkap subkelas Exception tidak termasuk RuntimeException (meskipun sebagian besar pengembang juga menangkap RuntimeException ... tetapi itu tidak pernah menjadi tujuan desainer bahasa).
Jika Anda menangkap OutOfMemoryError apa yang akan Anda lakukan? VM kehabisan memori, pada dasarnya yang dapat Anda lakukan hanyalah keluar. Anda bahkan mungkin tidak dapat membuka kotak dialog untuk memberi tahu mereka bahwa Anda kehabisan memori karena itu akan memakan memori :-)
VM melontarkan OutOfMemoryError ketika benar-benar kehabisan memori (memang semua Kesalahan seharusnya menunjukkan situasi yang tidak dapat dipulihkan) dan seharusnya tidak ada yang dapat Anda lakukan untuk menanganinya.
Hal yang harus dilakukan adalah mencari tahu mengapa Anda kehabisan memori (gunakan profiler, seperti yang ada di NetBeans) dan pastikan Anda tidak mengalami kebocoran memori. Jika Anda tidak mengalami kebocoran memori, tingkatkan memori yang Anda alokasikan ke VM.
sumber