Menangkap java.lang.OutOfMemoryError?

101

Dokumentasi untuk java.lang.Errorkata:

Error adalah subclass dari Throwable yang menunjukkan masalah serius yang tidak boleh ditangkap oleh aplikasi yang wajar

Tapi sebagai java.lang.Errorsubclass 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 OutOfMemoryErrorakan terlempar lagi.

Jadi, pertanyaan saya adalah:

  1. Apakah ada skenario dunia nyata saat menangkap java.lang.OutOfMemoryErrormungkin ide yang bagus?
  2. 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)?
Denis Bazhenov
sumber
Untuk pertanyaan pertama Anda, saya akan menambahkan bahwa saya akan menangkap OutOfMemoryError untuk (setidaknya mencoba) memberi tahu pengguna tentang masalah tersebut. Sebelumnya, error tidak tertangkap oleh klausa catch (Exception e), dan tidak ada masukan yang ditampilkan kepada pengguna.
Josep Rodríguez López
1
Ada beberapa kasus khusus, misalnya, mengalokasikan larik raksasa, di mana seseorang dapat menangkap kesalahan OOM di sekitar operasi itu dan memulihkannya dengan cukup baik. Tetapi menempatkan coba / tangkap di sekitar gumpalan kode besar dan mencoba memulihkan dengan bersih dan melanjutkan mungkin merupakan ide yang buruk.
Hot Licks

Jawaban:

85

Saya setuju dan tidak setuju dengan sebagian besar tanggapan di sini.

Ada sejumlah skenario di mana Anda mungkin ingin menangkap OutOfMemoryErrordan 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 Errordilempar, 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 OutOfMemoryErrortidak berarti bahwa JVM kehabisan memori di blok catch:

private static final int MEGABYTE = (1024*1024);
public static void runOutOfMemory() {
    MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    for (int i=1; i <= 100; i++) {
        try {
            byte[] bytes = new byte[MEGABYTE*500];
        } catch (Exception e) {
            e.printStackTrace();
        } catch (OutOfMemoryError e) {
            MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
            long maxMemory = heapUsage.getMax() / MEGABYTE;
            long usedMemory = heapUsage.getUsed() / MEGABYTE;
            System.out.println(i+ " : Memory Use :" + usedMemory + "M/" + maxMemory + "M");
        }
    }
}

Output dari kode ini:

1 : Memory Use :0M/247M
..
..
..
98 : Memory Use :0M/247M
99 : Memory Use :0M/247M
100 : Memory Use :0M/247M

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 menangkapnya Errorsetidaknya 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.

Chris
sumber
Setuju, saya akan memposting eksperimen saya pada jawaban baru.
Mister Smith
4
"Anda tidak pernah bisa 100% yakin bahwa JVM berada dalam status yang dapat diperbaiki": karena OutOfMemoryErrormungkin telah dilempar dari titik yang telah menempatkan program Anda dalam keadaan tidak konsisten, karena dapat dilempar kapan saja. Lihat stackoverflow.com/questions/8728866/…
Raedwald
Di OpenJdk1.7.0_40, saya tidak mendapatkan kesalahan atau pengecualian apa pun saat menjalankan kode ini. Bahkan saya mengubah MEGABYTE menjadi GIGABYTE (1024 * 1024 * 1024). Apakah itu karena pengoptimal menghapus variabel 'byte [] byte' karena tidak digunakan di sisa kode?
RoboAlex
"Ada sejumlah skenario di mana Anda mungkin ingin menangkap OutOfMemoryError" vs "Hanya ada satu alasan yang baik untuk menangkap OutOfMemoryError" . Buatlah pikiranmu !!!
Stephen C
3
Skenario dunia nyata ketika Anda MUNGKIN ingin menangkap kesalahan OutOfMemory: ketika ini disebabkan oleh mencoba mengalokasikan larik dengan lebih dari 2G elemen. Nama kesalahan sedikit keliru dalam kasus ini, tetapi masih OOM.
Charles Roth
31

Anda dapat memulihkannya:

package com.stackoverflow.q2679330;

public class Test {

    public static void main(String... args) {
        int size = Integer.MAX_VALUE;
        int factor = 10;

        while (true) {
            try {
                System.out.println("Trying to allocate " + size + " bytes");
                byte[] bytes = new byte[size];
                System.out.println("Succeed!");
                break;
            } catch (OutOfMemoryError e) {
                System.out.println("OOME .. Trying again with 10x less");
                size /= factor;
            }
        }
    }

}

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:

1: Apakah ada skenario kata nyata saat menangkap java.lang.OutOfMemoryError mungkin ide yang bagus?

Tidak ada yang terlintas dalam pikiran.

2: jika kita menangkap java.lang.OutOfMemoryError bagaimana kita bisa yakin bahwa catch handler tidak mengalokasikan memori dengan sendirinya (alat atau praktik terbaik apa pun)?

Tergantung pada apa yang menyebabkan OOME. Jika diumumkan di luar tryblok dan terjadi selangkah demi selangkah, maka peluang Anda kecil. Anda mungkin ingin memesan beberapa ruang memori sebelumnya:

private static byte[] reserve = new byte[1024 * 1024]; // Reserves 1MB.

dan kemudian setel ke nol selama OOME:

} catch (OutOfMemoryException e) {
     reserve = new byte[0];
     // Ha! 1MB free!
}

Tentu saja ini semua tidak masuk akal;) Berikan saja memori yang cukup JVM sesuai kebutuhan aplikasi Anda. Jalankan profiler jika perlu.

BalusC
sumber
1
Meskipun memesan ruang, tidak ada jaminan untuk solusi yang berfungsi. Ruang itu mungkin diambil oleh beberapa utas lain juga;)
Wolph
@ Wolph: Kalau begitu, beri JVM lebih banyak memori! O_o Semua dengan semua itu memang tidak masuk akal;)
BalusC
2
Potongan pertama berfungsi karena objek yang memicu kesalahan adalah objek BESAR tunggal (array). Saat klausa catch tercapai, klausa tersebut telah dikumpulkan oleh JVM yang haus memori. Jika Anda menggunakan objek yang sama di luar blok percobaan, atau di utas lain, atau dalam tangkapan yang sama, JVM tidak mengumpulkannya, sehingga tidak mungkin untuk membuat objek tunggal baru dalam bentuk apa pun. cuplikan kedua, misalnya, mungkin tidak berfungsi.
Mister Smith
3
mengapa tidak mengaturnya saja null?
Pacerier
1
@MisterSmith Komentar Anda tidak masuk akal. Objek besar tidak ada. Itu tidak dialokasikan sejak awal: itu memicu OOM, jadi itu pasti tidak membutuhkan GC.
Marquis dari Lorne
16

Secara umum, mencoba menangkap dan memulihkan dari OOM adalah ide yang buruk.

  1. 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.

  2. 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:

  • apa yang secara spesifik menyebabkan OOME,
  • apa yang dilakukan aplikasi pada saat itu, dan tidak masalah jika begitu saja membuang penghitungan itu, dan
  • bahwa OOME (secara kasar) serentak tidak dapat terjadi di utas lain.

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.)

Stephen C
sumber
Ya aku setuju denganmu. Secara umum, itu ide yang buruk. Tetapi mengapa saya memiliki kemungkinan untuk menangkapnya? :)
Denis Bazhenov
@dotsid - 1) karena ada kasus di mana Anda harus menangkapnya, dan 2) karena membuatnya tidak mungkin untuk menangkap OOM akan berdampak negatif pada bahasa dan / atau bagian lain dari runtime Java.
Stephen C
1
Anda berkata: "karena ada kasus di mana Anda harus menangkapnya". Jadi ini adalah bagian dari pertanyaan awal saya. Apa kasusnya ketika Anda ingin menangkap OOME?
Denis Bazhenov
1
@dotsid - lihat jawaban saya yang diedit. Satu-satunya kasus yang dapat saya pikirkan untuk menangkap OOM adalah ketika Anda perlu melakukan ini untuk memaksa aplikasi multi-utas keluar jika terjadi OOM. Anda mungkin ingin melakukan ini untuk semua subtipe Error.
Stephen C
1
Ini bukan masalah hanya menangkap OOME. Anda juga perlu memulihkannya. Bagaimana utas pulih jika seharusnya (katakanlah) memberi tahu utas lain ... tetapi utas mendapat OOME? Tentu, JVM tidak akan mati. Tetapi aplikasi kemungkinan akan berhenti bekerja karena utas macet menunggu pemberitahuan dari utas yang dimulai ulang karena menangkap OOME.
Stephen C
14

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 OutOfMemoryErrordan 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.)

Michael Kuhn
sumber
Jadi kamu punya kode seperti byte[] bytes = new byte[length]? Mengapa tidak memeriksa sizedi poin sebelumnya?
Raedwald
1
Karena hal yang sama sizeakan baik-baik saja dengan lebih banyak memori. Saya pergi melalui pengecualian karena dalam banyak kasus semuanya akan baik-baik saja.
Michael Kuhn
10

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.

Yishai
sumber
8

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:

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); 
bitmapOptions.inSampleSize = 1;
boolean imageSet = false;
while (!imageSet) {
  try {
    image = BitmapFactory.decodeFile(filePath, bitmapOptions);
    imageView.setImageBitmap(image); 
    imageSet = true;
  }
  catch (OutOfMemoryError e) {
    bitmapOptions.inSampleSize *= 2;
  }
}

Dengan cara ini saya berhasil menyediakan perangkat yang lebih dan kurang kuat sesuai dengan mereka, atau lebih tepatnya kebutuhan dan harapan penggunanya.

Maria
sumber
1
Pilihan lainnya adalah menghitung seberapa besar bitmap yang dapat Anda tangani alih-alih mencoba dan gagal. "Pengecualian harus digunakan untuk kasus luar biasa" - Saya pikir seseorang berkata. Tapi saya akan katakan, solusi Anda sepertinya jalan keluar termudah, mungkin bukan yang terbaik, tapi mungkin yang termudah.
jontejj
Itu tergantung pada codec. Bayangkan sebuah bmp sebesar 10MB mungkin hanya menghasilkan sedikit lebih dari 10MB heap, sementara JPEG 10MB akan "meledak". Sama dalam kasus saya di mana saya ingin mengurai XML yang dapat sangat bervariasi tergantung pada kompleksitas konten
Daniel Alder
5

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:

-XX:+PrintCommandLineFlags
-XX:+PrintConcurrentLocks
-XX:+PrintClassHistogram

Lihat daftar lengkapnya di sini

Rob Heiser
sumber
Ini bahkan lebih buruk dari yang Anda pikirkan. Karena OutOfMemeoryError dapat dilemparkan kapan saja dalam program Anda (tidak hanya dari newpernyataan) program Anda akan berada dalam status tidak ditentukan saat Anda menangkap pengecualian.
Raedwald
5

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:

boolean isOutOfMemory = false; // bendera digunakan untuk pelaporan
coba {
   SomeType largeVar;
   // Main loop yang mengalokasikan lebih banyak ke largeVar
   // dapat menghentikan OK, atau memunculkan OutOfMemoryError
}
catch (OutOfMemoryError ex) {
   // largeVar sekarang berada di luar jangkauan, begitu pula sampah
   System.gc (); // bersihkan data largeVar
   isOutOfMemory = true; // bendera tersedia untuk digunakan
}
// tanda tes program untuk melaporkan pemulihan

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.

Tony Simons
sumber
Dalam satu aplikasi berulir, jika Anda tidak lagi menggunakan beberapa objek baru yang bermasalah yang kreasinya menghasilkan kesalahan, ini mungkin dikumpulkan dalam klausa catch. Namun, jika JVM mendeteksi bahwa objek tersebut dapat digunakan nanti, itu tidak dapat dikumpulkan dan aplikasi meledak. Lihat jawaban saya di utas ini.
Mister Smith
4

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:

import java.util.LinkedList;
import java.util.List;

public class OOMErrorTest {             
    public static void main(String[] args) {
        List<Long> ll = new LinkedList<Long>();

        try {
            long l = 0;
            while(true){
                ll.add(new Long(l++));
            }
        } catch(OutOfMemoryError oome){         
            System.out.println("Error catched!!");
        }
        System.out.println("Test finished");
    }  
}

Namun, hanya menambahkan satu baris pada tangkapan akan menunjukkan kepada Anda apa yang saya bicarakan:

import java.util.LinkedList;
import java.util.List;

public class OOMErrorTest {             
    public static void main(String[] args) {
        List<Long> ll = new LinkedList<Long>();

        try {
            long l = 0;
            while(true){
                ll.add(new Long(l++));
            }
        } catch(OutOfMemoryError oome){         
            System.out.println("Error catched!!");
            System.out.println("size:" +ll.size());
        }
        System.out.println("Test finished");
    }
}

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 llyang 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 di System.out,printlnbaris), 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.

Tuan Smith
sumber
Bagaimana jika saya lakukan ll=nulldi dalam blok tangkapan?
Naanavanalla
3

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.

cHao
sumber
3

Untuk pertanyaan 2, saya sudah melihat solusi yang saya sarankan, oleh BalusC.

  1. Apakah ada skenario kata nyata saat menangkap java.lang.OutOfMemoryError mungkin merupakan ide yang bagus?

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.

Jarekczek
sumber
3

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.

Jörg Eisfeld
sumber
0
  1. Tergantung bagaimana Anda mendefinisikan "baik". Kami melakukan itu di aplikasi web buggy kami dan itu berfungsi sebagian besar waktu (untungnya, sekarang OutOfMemorytidak 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.
  2. Sejauh yang saya pahami, tumpukan tumpukan yang berat di jalan akan membatalkan begitu banyak referensi dan dengan demikian membebaskan begitu banyak memori sehingga Anda tidak perlu peduli tentang itu.

EDIT: Saya sarankan Anda mencobanya. Katakanlah, tulis program yang secara rekursif memanggil fungsi yang mengalokasikan lebih banyak memori secara progresif. Tangkap OutOfMemoryErrordan 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.

menggandakan
sumber
-1

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.

TofuBeer
sumber
7
Kesalahpahaman yang dipertahankan posting Anda adalah bahwa OOM menunjukkan JVM kehabisan memori. Sebaliknya, ini sebenarnya menunjukkan bahwa JVM tidak dapat mengalokasikan semua memori yang diinstruksikan. Artinya, jika JVM memiliki 10B ruang dan Anda 'baru' objek 100B, itu akan gagal, tapi Anda bisa berbalik dan 'baru' objek 5B dan baik-baik saja.
Tim Bender
1
Dan jika saya hanya membutuhkan 5B, mengapa saya meminta 10B? Jika Anda melakukan alokasi berdasarkan trial and error, Anda salah melakukannya.
TofuBeer
2
Saya kira maksud Tim Anda masih dapat melakukan beberapa pekerjaan bahkan dengan situasi OutOfMemory. Mungkin ada cukup memori tersisa untuk membuka dialog, misalnya.
Stephen Eilert