Java.lang.OutOfMemoryError'ı yakalıyor musunuz?


102

Belgeler için java.lang.Errordiyor ki:

Hata, makul bir uygulamanın yakalamaya çalışmaması gereken ciddi sorunları belirten bir Atılabilir alt sınıfıdır.

Ancak java.lang.Erroralt sınıfı olduğu gibi java.lang.Throwable, bu tür Fırlatılabilir'i yakalayabilirim.

Bu tür bir istisnayı yakalamanın neden iyi bir fikir olmadığını anlıyorum. Anladığım kadarıyla, yakalamaya karar verirsek, catch işleyici kendi başına herhangi bir bellek ayırmamalıdır. Aksi takdirde OutOfMemoryErrortekrar fırlatılacaktır.

Yani sorum şu:

  1. Yakalamanın java.lang.OutOfMemoryErroriyi bir fikir olabileceği gerçek dünya senaryoları var mı?
  2. Yakalamaya karar java.lang.OutOfMemoryErrorverirsek, yakalama işleyicisinin kendi başına herhangi bir bellek ayırmadığından nasıl emin olabiliriz (herhangi bir araç veya en iyi uygulama)?


İlk sorunuz için, sorunu kullanıcıya bildirmek (en azından denemek) için OutOfMemoryError'ı yakalayacağımı ekleyeceğim. Daha önce, hata catch (Exception e) cümlesi tarafından yakalanmıyordu ve kullanıcıya herhangi bir geri bildirim gösterilmiyordu.
Josep Rodríguez López

1
Örneğin, devasa bir dizi tahsis etmek gibi belirli durumlar vardır, bu işlemde OOM hatası yakalanır ve makul derecede iyi bir şekilde kurtarılabilir. Ancak, dene / yakala'yı büyük bir kod bloğunun etrafına yerleştirmek ve temiz bir şekilde kurtarmaya ve devam etmeye çalışmak muhtemelen kötü bir fikirdir.
Sıcak Licks

Yanıtlar:


86

Buradaki yanıtların çoğuna katılıyorum ve katılmıyorum.

Yakalamak isteyebileceğiniz birkaç senaryo vardır OutOfMemoryErrorve benim deneyimlerime göre (Windows ve Solaris JVM'lerde), yalnızca çok seyrek OutOfMemoryErrorolarak bir JVM'nin ölüm çanıdır.

Bir yakalamanın tek bir iyi nedeni vardır OutOfMemoryErrorve bu, nazikçe kapatmak, kaynakları temiz bir şekilde serbest bırakmak ve başarısızlığın nedenini yapabileceğiniz en iyi şekilde günlüğe kaydetmektir (eğer hala mümkünse).

Genel olarak, OutOfMemoryErroröbeğin kalan kaynakları ile tatmin edilemeyen bir blok bellek tahsisi nedeniyle oluşur.

Ne zaman Erroryığın başarısız ayırma daha önce olduğu gibi ayrılan nesnelerin aynı miktarda içerir ve şimdi çalışma zamanı temizlik için gerekli olabilir hatta daha fazla bellek boşaltmak için nesneleri başvurular damla zamanıdır atılır. Bu durumlarda, devam etmek bile mümkün olabilir, ancak bu kesinlikle kötü bir fikirdir çünkü JVM'nin onarılabilir bir durumda olduğundan asla% 100 emin olamazsınız.

Gösteri OutOfMemoryErrorJVM catch bloğunda bellek yetersiz olduğu anlamına gelmez:

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");
        }
    }
}

Bu kodun çıktısı:

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

Kritik bir şey çalıştırıyorsanız, genellikle yakalarım, Erroronu syserr'e kaydederim, ardından kendi seçtiğim günlüğe kaydetme çerçevemi kullanarak günlüğe kaydederim, sonra kaynakları serbest bırakmaya ve temiz bir şekilde kapatmaya devam ederim. Olabilecek en kötü şey nedir? JVM zaten ölüyor (veya çoktan ölüyor) ve Errororayı yakalayarak en azından bir temizleme şansı var.

Uyarı, bu tür hataların yakalanmasını yalnızca temizlemenin mümkün olduğu yerlerde hedeflemeniz gerektiğidir. Her yeri örtmeyin catch(Throwable t) {}ya da bunun gibi saçmalıklar.


Kabul ediyorum, deneyimi yeni bir cevap üzerine yayınlayacağım.
Mister Smith

4
"JVM'nin onarılabilir bir durumda olduğundan asla% 100 emin olamazsınız": çünkü, OutOfMemoryErrorprogramınızı tutarsız bir duruma getiren bir noktadan fırlatılmış olabilir, çünkü herhangi bir zamanda atılabilir . Stackoverflow.com/questions/8728866/… sayfasına
Raedwald

OpenJdk1.7.0_40'ta, bu kodu çalıştırdığımda herhangi bir hata veya istisna almıyorum. Hatta MEGABYTE'ı GIGABYTE (1024 * 1024 * 1024) olarak değiştirdim. Optimizer, kodun geri kalanında kullanılmadığı için 'bayt [] bayt' değişkenini kaldırdığı için mi?
RoboAlex

"OutOfMemoryError" a karşılık " OutOfMemoryError" a karşı " OutOfMemoryError" ı yakalamak isteyebileceğiniz birkaç senaryo vardır . Kararını ver!!!
Stephen C

3
OutOfMemory hatasını yakalamak isteyebileceğiniz gerçek dünya senaryosu: 2G'den daha fazla öğeye sahip bir dizi ayırmaya çalışmaktan kaynaklandığında. Hata adı bu durumda biraz yanlış bir isimdir, ancak yine de bir OOM'dir.
Charles Roth

31

Sen olabilir ondan kurtarmak:

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;
            }
        }
    }

}

Ama mantıklı geliyor mu? Başka ne yapmak isterdiniz? Neden başlangıçta bu kadar bellek ayırdınız? Daha az bellek de uygun mu? Neden zaten onu kullanmıyorsun? Ya da bu mümkün değilse, neden JVM'ye başından itibaren daha fazla bellek vermiyorsunuz?

Sorularınıza geri dönün:

1: java.lang.OutOfMemoryError'ı yakalarken iyi bir fikir olabilecek herhangi bir gerçek kelime senaryosu var mı?

Hiçbiri akla gelmiyor.

2: java.lang.OutOfMemoryError'ı yakalarsak, catch işleyicisinin kendi başına herhangi bir bellek ayırmadığından nasıl emin olabiliriz (herhangi bir araç veya en iyi uygulama)?

OOME'a neyin sebep olduğuna bağlıdır. tryBlok dışında ilan edildiyse ve adım adım gerçekleştiyse, şansınız azdır. Sen olabilir önceden miktar bellek alanını ayırmak istiyorum:

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

ve sonra OOME sırasında sıfıra ayarlayın:

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

Elbette bu hiç mantıklı değil;) JVM'ye uygulamanızın gerektirdiği kadar yeterli bellek verin. Gerekirse bir profil oluşturucu çalıştırın.


1
Yer ayırmak bile, çalışan bir çözüm için garanti vermez. Bu alan başka bir ileti dizisi tarafından da alınabilir;)
Wolph

@Wolph: O zaman JVM'ye daha fazla bellek verin! O_o Tüm bunların hiçbir anlamı yok;)
BalusC

2
İlk pasaj, hatayı tetikleyen nesne tek bir BÜYÜK nesne (dizi) olduğu için çalışır. Yakalama cümlesine ulaşıldığında, hafızaya aç JVM tarafından toplanmıştır. Aynı nesneyi try bloğunun dışında veya başka bir iş parçacığında veya aynı yakalamada kullanıyorsanız, JVM onu toplamaz ve herhangi bir türden yeni tek bir nesne oluşturmayı imkansız hale getirir. örneğin ikinci ön bilgi çalışmayabilir.
Mister Smith

3
neden basitçe ayarlamıyorsunuz null?
Pacerier

1
@MisterSmith Yorumunuz mantıklı değil. Büyük nesne mevcut değil. İlk etapta tahsis edilmedi: OOM'yi tetikledi, bu yüzden kesinlikle GC'ye ihtiyacı yok.
Marquis of Lorne

16

Genel olarak, bir OOM'yi yakalamaya ve kurtarmaya çalışmak kötü bir fikirdir.

  1. Bir OOME, uygulamanızın bilmediği iş parçacıkları da dahil olmak üzere diğer iş parçacıklarına da atılmış olabilir. Bu tür herhangi bir ileti dizisi şimdi ölmüş olacak ve bir bildirimi bekleyen herhangi bir şey sonsuza dek takılıp kalabilir. Kısacası, uygulamanız son olarak bozulabilir.

  2. Başarıyla kurtarsanız bile, JVM'niz hala yığın açlığı çekiyor olabilir ve sonuç olarak uygulamanız korkunç bir şekilde çalışacaktır.

Bir OOME ile yapılacak en iyi şey, JVM'nin ölmesine izin vermektir.

(Bu JVM varsayar yapar JVM öldürmez Tomcat servlet parçacığı üzerinde örneği OOMS için. Kalıp ve herhangi isteklerine değil cevap ... hatta istekleri olmaz nereye Tomcat'e bu potansiyel müşteriler bir rüyaya gidiyor tekrar başlat.)

DÜZENLE

OOM'u yakalamanın kötü bir fikir olduğunu söylemiyorum. Sorunlar, kasıtlı olarak veya gözetimle OOME'dan kurtarmaya çalıştığınızda ortaya çıkar. Bir OOM yakaladığınızda (doğrudan veya Hata veya Atılabilir alt türü olarak) onu yeniden atmanız veya uygulamanın / JVM'nin çıkmasını ayarlamanız gerekir.

Bir kenara: Bu, OOM'lar karşısında maksimum sağlamlık için bir uygulamanın , OOME hangi iş parçacığına atılırsa atılsın, bir OOME durumunda uygulamanın çıkmasına neden olacak bir işleyici ayarlamak için Thread.setDefaultUncaughtExceptionHandler () kullanması gerektiğini göstermektedir. Bu konudaki fikirlerle ilgilenirim ...

Diğer tek senaryo, OOM'nin herhangi bir teminat hasarı ile sonuçlanmadığından emin olduğunuz zamandır; yani biliyorsun:

  • OOME'ye özellikle ne sebep oldu,
  • o anda uygulamanın ne yapmakta olduğunu ve bu hesaplamayı basitçe iptal etmenin sorun olmadığını ve
  • (kabaca) eşzamanlı OOME başka bir iş parçacığında oluşmuş olamaz.

Bunları bilmenin mümkün olduğu uygulamalar vardır, ancak çoğu uygulama için bir OOME'dan sonra devam etmenin güvenli olduğundan emin olamazsınız. Denediğinizde deneysel olarak "işe yarasa" bile.

(Sorun, "beklenen" OOME'lerin sonuçlarının güvenli olduğunu ve "beklenmeyen" OOME'lerin bir dene / yakala OOME'nin kontrolü altında oluşamayacağını göstermek için resmi bir kanıt gerekmesidir.)


Evet ben size katılıyorum. Genel olarak kötü bir fikirdir. Ama neden onu yakalama imkanım var? :)
Denis Bazhenov

@dotsid - 1) çünkü onu yakalamanız gereken durumlar vardır ve 2) OOM'yi yakalamayı imkansız hale getirmek, dil ve / veya Java çalışma zamanının diğer bölümleri üzerinde olumsuz bir etkiye sahip olacaktır.
Stephen C

1
"Çünkü yakalamanız gereken durumlar var" diyorsunuz. Yani bu benim asıl sorumun bir parçasıydı. Ne sen yakalamak Gel istediğinizde durumlardır?
Denis Bazhenov

1
@dotsid - düzenlenmiş cevabıma bakın. OOM'yi yakalamak için düşünebildiğim tek durum , bir OOM durumunda çok iş parçacıklı bir uygulamayı çıkmaya zorlamak için bunu yapmanız gerektiği zamandır. Bunu tüm alt türleri için yapmak isteyebilirsiniz Error.
Stephen C

1
Bu sadece OOME'leri yakalamak meselesi değil. Ayrıca onlardan kurtulmanız gerekir. Nasıl bir iş parçacığı yok kurtarmak o (söz hakkından) Başka bir iş parçacığı bildirmek gerekiyordu eğer ... bu bir Gel var? Elbette, JVM ölmeyecek. Ancak, bir OOME yakalanması nedeniyle yeniden başlatılan iş parçacıklarından gelen bildirimleri bekleyen iş parçacıkları nedeniyle uygulama çalışmayı durdurabilir.
Stephen C

14

Evet, gerçek dünya senaryoları var. İşte benimki: Düğüm başına sınırlı belleğe sahip bir küme üzerinde çok sayıda öğenin veri kümelerini işlemem gerekiyor. Belirli bir JVM örnekleri birbiri ardına birçok öğeden geçer, ancak öğelerden bazıları kümede işlenemeyecek kadar büyüktür: OutOfMemoryErrorHangi öğelerin çok büyük olduğunu yakalayabilir ve not alabilirim. Daha sonra, daha fazla RAM içeren bir bilgisayarda yalnızca büyük öğeleri yeniden çalıştırabilirim.

(Başarısız olan bir dizinin tek bir çok gigabaytlık tahsisi olduğundan, JVM, hatayı yakaladıktan sonra hala iyi durumda ve diğer öğeleri işlemek için yeterli bellek var.)


Yani kodunuz var byte[] bytes = new byte[length]mı? Neden sizedaha erken bir noktada kontrol etmiyorsunuz ?
Raedwald

1
Çünkü aynı sizeşey daha fazla hafıza için iyi olacak. İstisnadan geçiyorum çünkü çoğu durumda her şey yoluna girecek.
Michael Kuhn

10

Bir OOME'u yakalamanın kesinlikle mantıklı olduğu senaryolar var. IDEA bunları yakalar ve başlangıç ​​belleği ayarlarını değiştirmenize izin veren bir iletişim kutusu açar (ve işiniz bittiğinde çıkar). Bir uygulama sunucusu bunları yakalayabilir ve bildirebilir. Bunu yapmanın anahtarı, bunu sevkıyatta yüksek bir seviyede yapmaktır, böylece istisnayı yakaladığınız noktada bir grup kaynağın serbest bırakılması için makul bir şansınız olur.

Yukarıdaki IDEA senaryosunun yanı sıra, genel olarak yakalama, yalnızca özel olarak OOM değil, Atılabilir olmalıdır ve en azından iş parçacığının kısa bir süre içinde sonlandırılacağı bir bağlamda yapılmalıdır.

Elbette çoğu zaman hafıza yetersizdir ve durum düzeltilemez, ancak bunun mantıklı olduğu yollar vardır.


8

Bu soruyla karşılaştım çünkü benim durumumda OutOfMemoryError'ı yakalamanın iyi bir fikir olup olmadığını merak ediyordum. Bu hatayı yakalamanın birisine (yani bana) mantıklı gelebileceğini ve kısmen de benim durumumda gerçekten iyi bir fikir olup olmadığını öğrenmek için başka bir örnek daha göstermek için cevap veriyorum (ben bir uber genç geliştirici olarak asla yapamam yazdığım tek bir kod satırından çok emin olun).

Her neyse, farklı bellek boyutlarına sahip farklı cihazlarda çalıştırılabilen bir Android uygulaması üzerinde çalışıyorum. Tehlikeli kısım, bir dosyadan bir bit eşlemin kodunu çözmek ve onu bir ImageView örneğinde görüntülemektir. Daha güçlü cihazları kodu çözülmüş bitmap boyutu açısından sınırlamak istemiyorum veya uygulamanın, hiç karşılaşmadığım çok düşük bellekle karşılaştığım eski bir cihazda çalıştırılmayacağından emin olamıyorum. Dolayısıyla bunu yapıyorum:

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;
  }
}

Bu şekilde onların veya kullanıcılarının ihtiyaç ve beklentilerine göre giderek daha az güçlü cihazlar sağlamayı başarıyorum.


1
Başka bir seçenek de, denemek ve başarısız olmak yerine ne kadar büyük bitmap'leri kullanabileceğinizi hesaplamak olacaktır. "İstisnai durumlar için istisnalar kullanılmalıdır" - Sanırım birisi dedi. Ancak şunu söyleyeceğim, çözümünüz en kolay çıkış yolu gibi görünüyor, belki de en iyisi değil, ama muhtemelen en kolayı.
jontejj

Codec bileşenine bağlıdır. 10MB'lik bir bmp'nin muhtemelen 10MB'den biraz daha fazla yığınla sonuçlandığını ve 10MB'lik bir JPEG'in "patlayacağını" hayal edin. İçeriğin karmaşıklığına bağlı olarak büyük ölçüde değişebilen bir XML ayrıştırmak istediğim durumumda da aynı
Daniel Alder

5

Evet, asıl soru "istisna işleyicide ne yapacaksınız?" Neredeyse yararlı olan her şey için daha fazla bellek ayıracaksınız. Bir OutOfMemoryError oluştuğunda bazı tanılama çalışmaları yapmak isterseniz -XX:OnOutOfMemoryError=<cmd>, HotSpot VM tarafından sağlanan kancayı kullanabilirsiniz . Bir OutOfMemoryError oluştuğunda komutlarınızı çalıştırır ve Java'nın yığınının dışında yararlı bir şey yapabilirsiniz. Uygulamanın hafızanın tükenmesini gerçekten önlemek istiyorsun, bu yüzden neden olduğunu bulmak ilk adım. Daha sonra MaxPermSize'nin yığın boyutunu uygun şekilde artırabilirsiniz. İşte diğer bazı yararlı HotSpot kancaları:

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

Tam listeyi burada görün


Düşündüğünden bile daha kötü. Bir Çünkü OutOfMemeoryError senin programında herhangi bir noktada atılmış olabilir (a dan sadece newdeyimi) Eğer exction yakalamak zaman programı tanımlanmamış bir durumda olacaktır.
Raedwald

5

OutOfMemoryError hatalarından kurtarılması gereken bir uygulamam var ve tek iş parçacıklı programlarda her zaman çalışıyor, ancak bazen çok iş parçacıklı programlarda çalışmıyor. Uygulama, üretilen test dizilerini test sınıflarında mümkün olan maksimum derinliğe kadar yürüten otomatik bir Java test aracıdır. Şimdi, kullanıcı arabiriminin kararlı olması gerekir, ancak test motorunun belleği, test senaryoları ağacını büyütürken yetersiz kalabilir. Bunu test motorundaki aşağıdaki türden kod deyimiyle hallediyorum:

boolean isOutOfMemory = false; // raporlama için kullanılan bayrak
Deneyin {
   SomeType largeVar;
   // largeVar'a giderek daha fazlasını ayıran ana döngü
   // Tamam'ı sonlandırabilir veya OutOfMemoryError'ı yükseltebilir
}
catch (OutOfMemoryError ex) {
   // largeVar artık kapsam dışı, çöp de öyle
   System.gc (); // largeVar verilerini temizle
   isOutOfMemory = true; // kullanıma uygun bayrak
}
// kurtarmayı raporlamak için bayrağı test et

Bu, tek iş parçacıklı uygulamalarda her zaman işe yarar. Ancak yakın zamanda test motorumu kullanıcı arayüzünden ayrı bir çalışan iş parçacığına koydum. Şimdi, bellek yetersizliği her iki iş parçacığında da keyfi olarak meydana gelebilir ve onu nasıl yakalayacağım benim için net değil.

Örneğin, kullanıcı arayüzümdeki animasyonlu bir GIF'in kareleri, kontrolüm dışında olan bir Swing sınıfı tarafından perde arkasında oluşturulan özel bir iş parçacığı tarafından döndürülürken OOME meydana geldi. İhtiyaç duyulan tüm kaynakları önceden tahsis ettiğimi düşünmüştüm, ama açıkça animatör bir sonraki görüntüyü her getirişinde bellek ayırıyor. Herkes büyüdü OOMEs nasıl işleneceği konusunda bir fikir varsa herhangi parçacığı, duymak isteriz.


Tek iş parçacıklı bir uygulamada, artık oluşturulması hataya neden olan bazı sorunlu yeni nesneleri kullanmıyorsanız, bunlar catch cümlesinde toplanabilir. Ancak, JVM nesnenin daha sonra kullanılabileceğini tespit ederse, toplanamaz ve uygulama patlar. Cevabımı bu başlıkta görün.
Mister Smith

4

OOME yakalanabilir, ancak JVM'nin yakalamaya ulaşıldığında bazı nesneleri toplayıp toplayıp toplayamadığına ve o zamana kadar kaç yığın bellek kaldığına bağlı olarak genellikle yararsız olacaktır.

Örnek: JVM'mde bu program tamamlanana kadar çalışıyor:

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");
    }  
}

Ancak, yakalamaya sadece tek bir satır eklemek, size neden bahsettiğimi gösterecektir:

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");
    }
}

İlk program sorunsuz çalışır çünkü yakalamaya ulaşıldığında, JVM listenin artık kullanılmayacağını algılar (Bu algılama aynı zamanda derleme zamanında yapılan bir optimizasyon olabilir). Böylece, yazdırma ifadesine ulaştığımızda, yığın belleği neredeyse tamamen serbest kaldı, bu nedenle artık devam etmek için geniş bir manevra marjına sahibiz. Bu en iyi durumdur.

Ancak kod, liste gibi llOOME yakalandıktan sonra kullanılacak şekilde düzenlenirse , JVM kodu toplayamaz. Bu ikinci parçada olur. Yeni bir Long yaratımıyla tetiklenen OOME yakalandı, ancak yakında yeni bir Nesne ( System.out,printlnsatırda bir String ) oluşturuyoruz ve yığın neredeyse dolu, bu yüzden yeni bir OOME atılıyor. Bu en kötü durum senaryosu: yeni bir nesne yaratmaya çalıştık, başarısız olduk, OOME'yi yakaladık, evet, ama şimdi yeni yığın belleği gerektiren ilk talimat (örneğin: yeni bir nesne oluşturma) yeni bir OOME atacak. Bir düşünün, bu noktada çok az hafıza kaldığında başka ne yapabiliriz? Muhtemelen çıkıyorum. Dolayısıyla işe yaramaz.

JVM'nin kaynakları toplamamasının sebeplerinden biri gerçekten korkutucudur: diğer iş parçacıklarıyla paylaşılan bir kaynak da ondan yararlanır. Beyni olan herkes, herhangi bir tür deneysel olmayan uygulamaya yerleştirildiğinde OOME'u yakalamanın ne kadar tehlikeli olabileceğini görebilir.

Windows x86 32bits JVM (JRE6) kullanıyorum. Her Java uygulaması için varsayılan bellek 64MB'dir.


Ya ll=nullyakalama bloğunun içinde yaparsam ?
Naanavanalla

3

OOM hatalarını yakalamanın nedenini düşünebilmemin tek nedeni, artık kullanmadığınız bazı büyük veri yapılarına sahip olmanız ve hafızanın bir kısmını boş ve boş olarak ayarlayabilmeniz olabilir. Ancak (1) bu, hafızanızı boşa harcadığınız anlamına gelir ve kodunuzu bir OOME'dan sonra topallamak yerine düzeltmeniz gerekir ve (2) yakalasanız bile ne yapardınız? OOM herhangi bir zamanda gerçekleşebilir ve potansiyel olarak her şeyin yarısı tamamlanmış halde kalır.


3

Soru 2 için BalusC tarafından önereceğim çözümü zaten görüyorum.

  1. Java.lang.OutOfMemoryError'ı yakalarken iyi bir fikir olabilecek herhangi bir gerçek kelime senaryosu var mı?

Sanırım iyi bir örnekle karşılaştım. Awt uygulaması mesajları gönderirken, yakalanmamış OutOfMemoryError stderr üzerinde görüntülenir ve mevcut mesajın işlenmesi durdurulur. Ancak uygulama çalışmaya devam ediyor! Kullanıcı yine de sahne arkasında meydana gelen ciddi sorunların farkında olmadan başka komutlar verebilir. Özellikle standart hatayı gözlemleyemediğinde veya gözlemlemediğinde. Bu nedenle, istisnayı yakalamak ve uygulamanın yeniden başlatılmasını sağlamak (veya en azından önermek) arzu edilen bir şeydir.


3

Sadece bir OutOfMemoryError yakalamanın mantıklı göründüğü ve işe yaradığı bir senaryom var.

Senaryo: Bir Android Uygulamasında, birden çok bitmap'i mümkün olan en yüksek çözünürlükte görüntülemek ve bunları akıcı bir şekilde yakınlaştırabilmek istiyorum.

Akıcı yakınlaştırma nedeniyle, bitmap'lerin bellekte olmasını istiyorum. Bununla birlikte, Android'in cihaza bağlı ve kontrol edilmesi zor olan bellek sınırlamaları vardır.

Bu durumda, bitmap okunurken OutOfMemoryError olabilir. Burada, onu yakalayıp daha düşük çözünürlükle devam etsem yardımcı oluyor.


0
  1. "İyi" yi nasıl tanımladığına bağlı. Bunu buggy web uygulamamızda yapıyoruz ve çoğu zaman işe yarıyor (şükürler olsun ki artık OutOfMemoryalakasız bir düzeltme nedeniyle olmuyor). Ancak, onu yakalasanız bile, yine de bazı önemli kodları kırmış olabilir: eğer birkaç iş parçacığınız varsa, bellek ayırma bunların herhangi birinde başarısız olabilir. Dolayısıyla, uygulamanıza bağlı olarak, hala geri dönüşü olmayan bir şekilde kırılma şansı% 10-90'dır.
  2. Anladığım kadarıyla, yoldaki ağır yığının çözülmesi pek çok referansı geçersiz kılacak ve bu nedenle o kadar çok hafıza boşa çıkaracak ki, bunu umursamamanız gerekir.

DÜZENLEME: Denemenizi öneririm. Diyelim ki, aşamalı olarak daha fazla bellek ayıran bir işlevi yinelemeli olarak çağıran bir program yazın. Yakalayın OutOfMemoryErrorve o noktadan anlamlı bir şekilde devam edip edemeyeceğinizi görün. Deneyimlerime göre, benim durumumda WebLogic sunucusu altında gerçekleşse de, bunu yapabileceksiniz, bu yüzden bazı kara büyü söz konusu olabilir.


-1

Throwable altında her şeyi yakalayabilirsiniz, genel olarak konuşursak, yalnızca RuntimeException hariç Exception alt sınıflarını yakalamalısınız (geliştiricilerin büyük bir kısmı da RuntimeException'ı yakalar ... ancak dil tasarımcılarının amacı bu değildi).

OutOfMemoryError'ı yakalarsan ne yapardın? VM'de bellek yetersiz, temelde yapabileceğiniz tek şey çıkmaktır. Muhtemelen onlara hafızanın yetersiz olduğunu söyleyen bir iletişim kutusu bile açamazsınız, çünkü bu hafıza gerektirir :-)

Sanal makine gerçekten bellek yetersiz olduğunda bir OutOfMemoryError atar (aslında tüm Hatalar kurtarılamaz durumları belirtmelidir) ve bununla başa çıkmak için gerçekten yapabileceğiniz hiçbir şey olmamalıdır.

Yapılması gerekenler, belleğinizin neden tükendiğini bulmak (NetBeans'tekine benzer bir profil oluşturucu kullanın) ve bellek sızıntısı olmadığından emin olmaktır. Bellek sızıntılarınız yoksa, VM'ye ayırdığınız belleği artırın.


7
Gönderinizin devam ettirdiği bir yanılgı, OOM'un JVM'nin hafızasının yetersiz olduğunu göstermesidir. Bunun yerine, JVM'nin kendisine talimat verildiği tüm belleği ayıramadığını gösterir. Yani, JVM'de 10B alan varsa ve bir 100B nesneyi 'yenilerseniz', bu başarısız olur, ancak bir 5B nesnesini geri döndürebilir ve 'yenileyebilirsiniz' ve iyi olabilirsiniz.
Tim Bender

1
Ve sadece 5B'ye ihtiyacım olsaydı, neden 10B istiyorum? Deneme yanılmaya dayalı tahsis yapıyorsanız, yanlış yapıyorsunuz demektir.
TofuBeer

2
Sanırım Tim, OutOfMemory durumunda bile bazı işler yapabileceğiniz anlamına geliyordu. Örneğin, bir diyalog açmak için yeterli bellek kalmış olabilir.
Stephen Eilert
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.