कैचिंग java.lang.OutOfMemoryError?


101

दस्तावेज़ के लिए java.lang.Errorकहता है:

एक त्रुटि थ्रोबल का एक उपवर्ग है जो गंभीर समस्याओं को इंगित करता है कि एक उचित अनुप्रयोग को पकड़ने की कोशिश नहीं करनी चाहिए

लेकिन जैसा java.lang.Errorकि एक उपवर्ग है java.lang.Throwable, मैं इस प्रकार के थ्रोएबल को पकड़ सकता हूं।

मैं समझता हूं कि इस प्रकार के अपवाद को पकड़ना एक अच्छा विचार क्यों नहीं है। जहां तक ​​मैं समझता हूं, अगर हम इसे पकड़ने का फैसला करते हैं, तो कैच हैंडलर को खुद से कोई मेमोरी आवंटित नहीं करनी चाहिए। अन्यथा OutOfMemoryErrorफिर से फेंक दिया जाएगा।

तो, मेरा सवाल है:

  1. क्या कोई वास्तविक दुनिया के परिदृश्य हैं जब पकड़ना java.lang.OutOfMemoryErrorएक अच्छा विचार हो सकता है?
  2. यदि हम पकड़ने का फैसला करते हैं java.lang.OutOfMemoryError, तो हम यह कैसे सुनिश्चित कर सकते हैं कि कैच हैंडलर खुद से कोई मेमोरी आवंटित न करे (कोई भी टूल या सर्वश्रेष्ठ प्रैक्टिस)?


आपके पहले प्रश्न के लिए, मैं यह जोड़ूंगा कि मैं समस्या के उपयोगकर्ता को सूचित करने के लिए (कम से कम प्रयास करने के लिए) आउटऑफ़मैरीऑयर को पकड़ लूंगा। पहले, त्रुटि पकड़ (अपवाद ई) क्लॉज़ द्वारा नहीं पकड़ी गई थी, और उपयोगकर्ता को कोई प्रतिक्रिया नहीं दिखाई गई थी।
जोसेप रोड्रिग्ज लोपेज

1
विशिष्ट मामले हैं, उदाहरण के लिए, एक विशाल सरणी आवंटित करना, जहां कोई उस ऑपरेशन के आसपास ओओएम त्रुटि को पकड़ सकता है और यथोचित रूप से ठीक हो सकता है। लेकिन कोड की एक बड़ी बूँद के आसपास की कोशिश / पकड़ को साफ और ठीक से जारी रखने का प्रयास करना संभवतः एक बुरा विचार है।
हॉट लिक्स

यह भी देखें stackoverflow.com/questions/14376924/...
Raedwald

जवाबों:


85

मैं सहमत हूं और यहां अधिकांश प्रतिक्रियाओं से असहमत हूं।

ऐसे कई परिदृश्य हैं, जहाँ आप OutOfMemoryError(Windows और Solaris OutOfMemoryErrorJVM पर) अपने अनुभव और कैच पकड़ने की इच्छा कर सकते हैं , केवल बहुत ही आसानी से एक JVM की मृत्यु हो जाती है।

एक को पकड़ने के लिए केवल एक अच्छा कारण है OutOfMemoryErrorऔर वह है कि सुंदर ढंग से बंद करना, संसाधनों को निर्बाध रूप से छोड़ना और असफलता का कारण जो आप कर सकते हैं (यदि ऐसा करना अभी भी संभव है)।

सामान्य तौर पर, OutOfMemoryErrorब्लॉक मेमोरी आवंटन के कारण होता है जो ढेर के शेष संसाधनों से संतुष्ट नहीं हो सकता है।

जब Errorढेर को फेंक दिया जाता है, तो आवंटित आवंटन से पहले के रूप में आवंटित वस्तुओं की एक ही राशि होती है और अब रन-टाइम ऑब्जेक्ट्स के संदर्भ को छोड़ने के लिए और भी अधिक मेमोरी को खाली करने के लिए समय होता है जो सफाई के लिए आवश्यक हो सकता है। इन मामलों में, इसे जारी रखना भी संभव हो सकता है लेकिन यह निश्चित रूप से एक बुरा विचार होगा क्योंकि आप कभी भी 100% निश्चित नहीं हो सकते हैं कि जेवीएम एक पुन: प्रयोज्य स्थिति में है।

प्रदर्शन का OutOfMemoryErrorमतलब यह नहीं है कि JVM कैच ब्लॉक में मेमोरी से बाहर है:

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

इस कोड का आउटपुट:

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

अगर कुछ महत्वपूर्ण चल रहा है, तो मैं आमतौर पर Errorइसे पकड़ता हूं, इसे सीज़र में लॉग इन करता हूं, फिर अपनी पसंद के लॉगिंग ढांचे का उपयोग करके इसे लॉग करता हूं, फिर संसाधनों को जारी करने और एक स्वच्छ फैशन में बंद करने के लिए आगे बढ़ता हूं। सबसे बुरा क्या हो सकता है? जेवीएम वैसे भी मर रहा है (या पहले से ही मर रहा है) और Errorवहां पकड़ने से कम से कम सफाई का मौका मिलता है।

चेतावनी यह है कि आपको इस प्रकार की त्रुटियों को केवल उन स्थानों पर पकड़ना है, जहाँ सफाई संभव है। catch(Throwable t) {}हर जगह कंबल या इस तरह बकवास मत करो ।


सहमत हूं, मैं अपने प्रयोग को एक नए उत्तर पर पोस्ट करूंगा।
मिस्टर स्मिथ

4
"आप कभी भी 100% निश्चित नहीं हो सकते हैं कि जेवीएम एक पुन: प्रयोज्य अवस्था में है": क्योंकि OutOfMemoryErrorहो सकता है कि आपको एक बिंदु से फेंक दिया गया हो, जिसने yout प्रोग्राम को असंगत स्थिति में रखा हो, क्योंकि इसे किसी भी समय फेंका जा सकता है । देखें stackoverflow.com/questions/8728866/…
Raedwald

OpenJdk1.7.0_40 में, मुझे इस कोड को चलाने पर कोई त्रुटि या अपवाद नहीं मिलता है। यहां तक ​​कि मैंने MEGABYTE को GIGABYTE (1024 * 1024 * 1024) में बदल दिया। क्या ऐसा इसलिए है क्योंकि ऑप्टिमाइज़र वैरिएबल 'बाइट [] बाइट्स को हटा देता है क्योंकि इसका इस्तेमाल बाकी कोड में नहीं किया जाता है?
रोबोएलेक्स

"ऐसे कई परिदृश्य हैं जहाँ आप एक आउटऑफ़मेरीइर्रर को पकड़ना चाह सकते हैं" बनाम " आउटऑफ़मेरीइरॉर्स को पकड़ने का केवल एक अच्छा कारण है" । मन बना लो!!!
स्टीफन सी

3
जब आप MAY आउटऑफ़मैमोरी त्रुटि पकड़ना चाहते हैं तो वास्तविक-विश्व परिदृश्य: जब यह 2G से अधिक तत्वों के साथ एक सरणी आवंटित करने की कोशिश के कारण होता है। त्रुटि नाम इस मामले में एक मिथ्या नाम है, लेकिन यह अभी भी एक OOM है।
चार्ल्स रोथ

31

आप इससे उबर सकते हैं :

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

}

लेकिन क्या यह समझ में आता है? आप और क्या करना चाहेंगे? आप शुरुआत में मेमोरी का इतना हिस्सा क्यों आवंटित करेंगे? क्या कम मेमोरी भी ठीक है? आप पहले से ही इसका उपयोग क्यों नहीं करते? या अगर यह संभव नहीं है, तो बस शुरुआत से ही जेवीएम को अधिक स्मृति क्यों नहीं देनी चाहिए?

अपने प्रश्नों पर वापस जाएं:

1: क्या java.lang.OutOfMemoryError को पकड़ने पर कोई वास्तविक शब्द परिदृश्य अच्छा विचार हो सकता है?

किसी के दिमाग में नहीं आता।

2: अगर हम java.lang.OutOfMemoryError को पकड़ते हैं, तो हम यह कैसे सुनिश्चित कर सकते हैं कि हैंडलर को पकड़ना अपने आप में किसी भी मेमोरी (किसी भी टूल या बेस्ट प्रैक्टिसेस) को आवंटित नहीं करता है?

निर्भर करता है कि OOME के ​​कारण क्या हुआ है। यदि यह tryब्लॉक के बाहर घोषित किया गया है और यह चरण-दर-चरण हुआ है, तो आपकी संभावना कम है। आप कर सकते हैं पहले से कुछ स्मृति स्थान आरक्षित करना चाहते हैं:

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

और फिर इसे OOME के ​​दौरान शून्य पर सेट करें:

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

बेशक यह सब बिना किसी मतलब के साथ करता है;) जेवीएम को पर्याप्त स्मृति दें क्योंकि आपके अपीयरेंस की आवश्यकता होती है। यदि आवश्यक हो तो एक प्रोफाइलर चलाएं।


1
यहां तक ​​कि अंतरिक्ष के लिए यह काम कर रहे समाधान के लिए कोई गारंटी नहीं है। वह स्थान कुछ अन्य धागे द्वारा भी लिया जा सकता है;)
वोल्फ

@Wolph: फिर JVM को अधिक मेमोरी दें! O_o ऑल विथ ऑल
इट्स

2
पहला स्निपेट काम करता है क्योंकि त्रुटि को ट्रिगर करने वाली वस्तु एकल बिग ऑब्जेक्ट (सरणी) है। कैच क्लॉज पहुंचने पर, यह मेमोरी-भूख JVM द्वारा एकत्र किया गया है। यदि आप कोशिश ब्लॉक के बाहर एक ही ऑब्जेक्ट का उपयोग कर रहे थे, या अन्य थ्रेड में, या एक ही पकड़ में, JVM इसे इकट्ठा नहीं करता है, जिससे किसी भी तरह का एक नया सिंगल ऑब्जेक्ट बनाना असंभव हो जाता है। उदाहरण के लिए दूसरा स्निपेट काम नहीं कर सकता है।
मिस्टर स्मिथ

3
बस इसे करने के लिए सेट क्यों नहीं null?
पचेरियर

1
@ मिस्टरस्मिथ आपकी टिप्पणी का कोई मतलब नहीं है। बड़ी वस्तु मौजूद नहीं है। यह पहली जगह में आवंटित नहीं किया गया था: यह OOM को ट्रिगर करता है, इसलिए इसे निश्चित रूप से GC की आवश्यकता नहीं है।
लोर्ने

15

सामान्य तौर पर, OOM से पकड़ने और पुनर्प्राप्त करने का प्रयास करना एक बुरा विचार है।

  1. एक थ्रेड को अन्य थ्रेड्स पर भी फेंका जा सकता है, जिसमें थ्रेड्स भी शामिल हैं जिनके बारे में आपके एप्लिकेशन को भी पता नहीं है। ऐसा कोई भी धागा अब मृत हो जाएगा, और एक सूचना पर इंतजार कर रहा कुछ भी हमेशा के लिए अटक सकता है। संक्षेप में, आपका ऐप टर्मिनली रूप से टूट सकता है।

  2. यहां तक ​​कि अगर आप सफलतापूर्वक पुनर्प्राप्त करते हैं, तो आपका जेवीएम अभी भी ढेर भुखमरी से पीड़ित हो सकता है और आपका आवेदन परिणाम के रूप में असामान्य रूप से प्रदर्शन करेगा।

OOME के ​​साथ सबसे अच्छी बात JVM को मरने देना है।

(यह है कि JVM मान लिया करता मरते हैं। एक बिलाव सर्वलेट धागे पर उदाहरण ooms लिए JVM मत मारो, और एक तानप्रतिष्टम्भी राज्य में जा रहा बिलाव को यह सुराग जहां यह किसी भी अनुरोध का जवाब नहीं ... यहां तक कि जाने का अनुरोध करने नहीं होंगे पुनः आरंभ करें।)

संपादित करें

मैं यह नहीं कह रहा हूं कि ओओएम को पकड़ना एक बुरा विचार है। समस्या तब उत्पन्न होती है जब आप ओओएम से, या तो जानबूझकर या ओवरसाइट से उबरने का प्रयास करते हैं। जब भी आप OOM को पकड़ते हैं (सीधे, या एरर या थ्रोबेबल के उपप्रकार के रूप में) तो आपको या तो इसे रीथ्रो करना चाहिए, या एप्लिकेशन / JVM से बाहर निकलने की व्यवस्था करनी चाहिए।

एक तरफ: यह बताता है कि OOMs के चेहरे पर अधिकतम मजबूती के लिए किसी अनुप्रयोग को एक हैंडलर सेट करने के लिए Thread.setDefaultUncaughtExceptionHandler () का उपयोग करना चाहिए जो अनुप्रयोग को OOME की स्थिति में बाहर निकलने का कारण बना देगा, फिर चाहे कोई भी धागा हो ओन को फेंक दिया गया हो। मैं इस पर राय में दिलचस्पी होगी ...

एकमात्र अन्य परिदृश्य तब होता है जब आप यह सुनिश्चित करने के लिए जानते हैं कि ओओएम के परिणामस्वरूप कोई संपार्श्विक क्षति नहीं हुई है; आप जानते हैं:

  • क्या विशेष रूप से OOME के ​​कारण,
  • उस समय आवेदन क्या कर रहा था, और यह कि उस गणना को छोड़ देना ठीक है, और
  • यह कि (लगभग) एक साथ OOME दूसरे धागे पर नहीं हो सकता है।

ऐसे अनुप्रयोग हैं जहां इन चीजों को जानना संभव है, लेकिन अधिकांश अनुप्रयोगों के लिए आप यह सुनिश्चित नहीं कर सकते हैं कि OOME सुरक्षित होने के बाद निरंतरता है। यहां तक ​​कि अगर यह कोशिश करता है कि यह "काम करता है"।

(समस्या यह है कि यह दिखाने के लिए एक औपचारिक प्रमाण की आवश्यकता है कि "प्रत्याशित" OOME के ​​परिणाम सुरक्षित हैं, और यह कि "अप्रत्याशित" OOME किसी भी कोशिश / OOME को पकड़ने के नियंत्रण में नहीं हो सकता है।)


हाँ, मैं आपसे सहमत हूँ। सामान्य तौर पर, यह बुरा विचार है। लेकिन फिर मुझे इसे पकड़ने की संभावना क्यों है? :)
डेनिस बझेनोव

@dotsid - 1) क्योंकि ऐसे मामले हैं जहां आपको इसे पकड़ना चाहिए, और 2) क्योंकि OOM को पकड़ना असंभव बना देता है और जावा रनटाइम के भाषा और / या अन्य हिस्सों पर नकारात्मक प्रभाव पड़ेगा।
स्टीफन सी

1
आप कहते हैं: "क्योंकि ऐसे मामले हैं जहां आपको इसे पकड़ना चाहिए"। तो यह मेरे मूल प्रश्न का हिस्सा था। जब आप ओमो को पकड़ना चाहते हैं तो क्या मामले हैं?
डेनिस बझेनोव

1
@dotsid - मेरा संपादित उत्तर देखें। ओओएम को पकड़ने के लिए मैं केवल एक ही मामला सोच सकता हूं, जब आपको ओओएम की स्थिति में बाहर निकलने के लिए बहु-थ्रेडेड एप्लिकेशन को बाध्य करने के लिए ऐसा करने की आवश्यकता होती है । आप शायद सभी उपप्रकारों के लिए ऐसा करना चाहते हैं Error
स्टीफन C

1
यह सिर्फ OOME को पकड़ने की बात नहीं है। आपको उनसे उबरने की भी जरूरत है। यदि एक धागा किसी अन्य धागे को सूचित करता है, तो उसे कैसे ठीक किया जाता है ... लेकिन यह एक OOME मिला? निश्चित रूप से, JVM नहीं मरेगा। लेकिन एप्लिकेशन को थ्रेड्स के नोटिफिकेशन के इंतजार में अटके हुए थ्रेड्स के कारण काम बंद करने की संभावना है, जो किसी ओओएम को पकड़ने के कारण पुनरारंभ होते हैं।
स्टीफन सी

14

हां, वास्तविक दुनिया के परिदृश्य हैं। यहाँ मेरा है: मुझे प्रति नोड सीमित मेमोरी वाले क्लस्टर पर बहुत सी वस्तुओं के डेटा सेट को संसाधित करने की आवश्यकता है। एक दिए गए JVM इंस्टेंसेस एक के बाद एक कई आइटमों से गुजरते हैं, लेकिन कुछ आइटम क्लस्टर पर प्रोसेस करने के लिए बहुत बड़े हैं: मैं उन OutOfMemoryErrorचीजों को पकड़ सकता हूं और नोट कर सकता हूं कि कौन सी चीजें बहुत बड़ी हैं। बाद में, मैं अधिक रैम वाले कंप्यूटर पर सिर्फ बड़ी वस्तुओं को फिर से चला सकता हूं।

(क्योंकि यह विफल रहने वाले एक सरणी का एक एकल बहु-गीगाबाइट आवंटन है, त्रुटि को पकड़ने के बाद भी जेवीएम ठीक है और अन्य वस्तुओं को संसाधित करने के लिए पर्याप्त मेमोरी है।)


तो आप की तरह कोड है byte[] bytes = new byte[length]? सिर्फ sizeपहले के बिंदु पर जांच क्यों नहीं ?
राएडवाल्ड

1
क्योंकि वही sizeअधिक मेमोरी के साथ ठीक होगा। मैं अपवाद से गुजरता हूं क्योंकि ज्यादातर मामलों में सबकुछ ठीक हो जाएगा।
माइकल कुह्न

10

निश्चित रूप से ऐसे परिदृश्य हैं जहां किसी को पकड़ने से कोई मतलब होता है। IDEA उन्हें पकड़ता है और आपको स्टार्टअप मेमोरी सेटिंग्स को बदलने के लिए एक डायलॉग पॉप अप करता है (और तब किया जाता है जब आप काम करते हैं)। एक एप्लिकेशन सर्वर उन्हें पकड़ सकता है और रिपोर्ट कर सकता है। ऐसा करने की कुंजी इसे प्रेषण पर उच्च स्तर पर करना है ताकि आपके पास संसाधनों का एक गुच्छा होने का एक उचित मौका हो, जहां आप अपवाद को पकड़ रहे हैं।

उपरोक्त आईडीईए परिदृश्य के अलावा, सामान्य रूप से कैचिंग थ्रोबेबल का होना चाहिए, न कि केवल विशेष रूप से ओओएम, और इसे एक संदर्भ में किया जाना चाहिए जहां कम से कम थ्रेड शीघ्र ही समाप्त हो जाएगा।

बेशक ज्यादातर बार स्मृति को भूखा रखा जाता है और स्थिति ठीक नहीं होती है, लेकिन ऐसे तरीके हैं जो इसे समझ में आते हैं।


8

मैं इस सवाल के कारण आया क्योंकि मैं सोच रहा था कि क्या मेरे मामले में आउटऑफ़मेरीऑरर को पकड़ना एक अच्छा विचार है। मैं यहां आंशिक रूप से एक और उदाहरण दिखाने के लिए जवाब दे रहा हूं जब इस त्रुटि को पकड़ना किसी (यानी मेरे) को समझ सकता है और आंशिक रूप से यह पता लगा सकता है कि क्या यह वास्तव में मेरे मामले में एक अच्छा विचार है (मेरे साथ एक uber जूनियर डेवलपर होने के नाते मैं कभी नहीं कर सकता हूं मेरे द्वारा लिखी गई कोड की किसी भी एक पंक्ति के बारे में बहुत सुनिश्चित हो)।

वैसे भी, मैं एक एंड्रॉइड एप्लिकेशन पर काम कर रहा हूं जिसे विभिन्न डिवाइसों पर अलग-अलग मेमोरी साइज के साथ चलाया जा सकता है। खतरनाक भाग एक फ़ाइल से बिटमैप को डिकोड कर रहा है और इसे ImageView उदाहरण में विस्थापित कर रहा है। मैं डिकोड किए गए बिटमैप के आकार के संदर्भ में अधिक शक्तिशाली उपकरणों को प्रतिबंधित नहीं करना चाहता हूं, और न ही यह सुनिश्चित कर सकता है कि ऐप को किसी ऐसे प्राचीन डिवाइस पर नहीं चलाया जाएगा जिसे मैंने बहुत कम मेमोरी के साथ कभी नहीं देखा है। इसलिए मैं ऐसा करता हूं:

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

इस तरह मैं उनके या उनके उपयोगकर्ताओं की जरूरतों और अपेक्षाओं के अनुसार अधिक और कम शक्तिशाली उपकरणों के लिए प्रदान करने का प्रबंधन करता हूं।


1
एक और विकल्प यह होगा कि आप कोशिश करने और असफल होने के बजाय कितने बड़े बिटमैप को संभाल सकते हैं। "अपवादों का उपयोग असाधारण मामलों के लिए किया जाना चाहिए" - मुझे लगता है कि किसी ने कहा। लेकिन मैं कहूंगा, आपका समाधान सबसे आसान तरीका है, शायद सबसे अच्छा नहीं है, लेकिन शायद सबसे आसान है।
जोंतेजज

यह कोडेक पर निर्भर करता है। 10MB के bmp की कल्पना करें, शायद परिणाम केवल 10MB से थोड़ा अधिक है, जबकि 10MB का JPEG "विस्फोट" करेगा। मेरे मामले में समान है जहां मैं एक XML को पार्स करना चाहता हूं जो सामग्री की जटिलता के आधार पर बड़े पैमाने पर भिन्न हो सकता है
डैनियल एल्डर

5

हाँ, असली सवाल यह है कि आप अपवाद हैंडलर में क्या करने जा रहे हैं? लगभग कुछ भी उपयोगी के लिए, आप अधिक मेमोरी आवंटित करेंगे। यदि आप एक OutOfMemoryError होने पर कुछ नैदानिक ​​कार्य करना चाहते हैं, तो आप -XX:OnOutOfMemoryError=<cmd>हॉटस्पॉट VM द्वारा दिए गए हुक का उपयोग कर सकते हैं । जब OutOfMemoryError होती है, तो यह आपकी कमांड को निष्पादित करेगा, और आप जावा के ढेर के बाहर कुछ उपयोगी काम कर सकते हैं। आप वास्तव में पहली जगह में मेमोरी से बाहर चलाने से एप्लिकेशन को रखना चाहते हैं, इसलिए यह पता लगाना कि ऐसा क्यों होता है पहला कदम है। फिर आप उपयुक्त के रूप में MaxPermSize के ढेर का आकार बढ़ा सकते हैं। यहां कुछ अन्य उपयोगी हॉटस्पॉट हुक दिए गए हैं:

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

पूरी सूची देखें यहां


यह आपके विचार से भी बदतर है। क्योंकि एOutOfMemeoryError आपके कार्यक्रम में किसी भी बिंदु पर फेंक दिया जा सकता है (न केवल एक newबयान से) आपका कार्यक्रम अपरिभाषित स्थिति में होगा जब आप छूट को पकड़ लेंगे।
रायडवल

5

मेरे पास एक एप्लिकेशन है जिसे आउटऑफ़मेरीऑरर विफलताओं से उबरने की आवश्यकता है, और एकल-थ्रेडेड प्रोग्राम में यह हमेशा काम करता है, लेकिन कभी-कभी मल्टी-थ्रेडेड प्रोग्राम में नहीं होता है। आवेदन एक स्वचालित जावा परीक्षण उपकरण है जो परीक्षण कक्षाओं पर अधिकतम संभव गहराई तक उत्पन्न परीक्षण अनुक्रमों को निष्पादित करता है। अब, UI स्थिर होना चाहिए, लेकिन परीक्षण मामलों का पेड़ बढ़ने के दौरान परीक्षण इंजन मेमोरी से बाहर चला सकता है। मैं परीक्षण इंजन में निम्नलिखित प्रकार के कोड मुहावरे से इसे संभालता हूं:

बूलियन isOutOfMemory = false; // ध्वज का उपयोग रिपोर्टिंग के लिए किया जाता है
प्रयत्न {
   किसी प्रकार लार्जविअर;
   // मुख्य लूप जो अधिक से अधिक लार्जवार को आवंटित करता है
   // ठीक को समाप्त कर सकता है, या OutOfMemoryError बढ़ा सकता है
}
पकड़ (OutOfMemoryError पूर्व) {
   // लार्जवायर अब दायरे से बाहर है, इसलिए कचरा है
   System.gc (); // लार्जवार डेटा को साफ करें
   isOutOfMemory = true; // ध्वज उपयोग के लिए उपलब्ध
}
// कार्यक्रम परीक्षण रिपोर्ट पुनर्प्राप्ति की रिपोर्ट करता है

यह हर बार सिंगल-थ्रेडेड एप्लिकेशन में काम करता है। लेकिन मैंने हाल ही में अपने परीक्षण इंजन को UI से एक अलग कार्यकर्ता-थ्रेड में डाल दिया। अब, मेमोरी में से किसी भी धागे में मनमाने ढंग से हो सकता है, और यह मुझे स्पष्ट नहीं है कि इसे कैसे पकड़ा जाए।

उदाहरण के लिए, मेरे पास कुछ ऐसा था जब मेरे UI में एक एनिमेटेड GIF के फ्रेम को एक मालिकाना धागे द्वारा साइकल किया जा रहा था जो कि एक स्विंग क्लास द्वारा पर्दे के पीछे बनाया गया है जो मेरे नियंत्रण से बाहर है। मैंने सोचा था कि मैंने पहले से आवश्यक सभी संसाधनों को आवंटित किया था, लेकिन स्पष्ट रूप से एनिमेटर हर बार अगली छवि लाने पर मेमोरी आवंटित कर रहा है। अगर किसी को इस बात का अंदाजा है कि किसी भी धागे में उठाए गए ओमोन्स को कैसे संभालना है , तो मुझे सुनना अच्छा लगेगा।


एक एकल थ्रेडेड ऐप में, यदि आप अब कुछ समस्याग्रस्त नई वस्तुओं का उपयोग नहीं कर रहे हैं, जिनके निर्माण में त्रुटि हुई है, तो उन्हें पकड़ क्लॉज में एकत्र किया जा सकता है। हालाँकि, अगर JVM यह पता लगाता है कि वस्तु का उपयोग बाद में किया जा सकता है, तो इसे एकत्र नहीं किया जा सकता है और ऐप चल जाता है। इस सूत्र में मेरा उत्तर देखें।
मिस्टर स्मिथ

4

ओओएम को पकड़ा जा सकता है लेकिन आम तौर पर बेकार होने वाला है, इस पर निर्भर करता है कि क्या जेवीएम कुछ वस्तुओं को इकट्ठा करने में सक्षम होता है, जब पकड़ में आ जाता है, और उस समय तक कितनी ढेर मेमोरी बच जाती है।

उदाहरण: मेरे जेवीएम में, यह कार्यक्रम पूरा होने तक चलता है:

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

हालाँकि, कैच पर केवल एक लाइन जोड़ने से आपको पता चल जाएगा कि मैं किस बारे में बात कर रहा हूँ:

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

पहला कार्यक्रम ठीक चलता है क्योंकि जब पकड़ में आता है, तो जेवीएम यह पता लगाता है कि सूची का अब उपयोग नहीं किया जा रहा है (यह पता लगाना संकलन समय पर किया गया अनुकूलन भी हो सकता है)। इसलिए जब हम प्रिंट स्टेटमेंट तक पहुंचते हैं, तो ढेर मेमोरी को लगभग पूरी तरह से मुक्त कर दिया गया है, इसलिए अब जारी रखने के लिए हमारे पास पैंतरेबाज़ी का एक विस्तृत मार्जिन है। यह सबसे अच्छा मामला है।

हालाँकि, यदि कोड को व्यवस्थित llकिया जाता है जैसे कि OOME पकड़े जाने के बाद सूची का उपयोग किया जाता है, तो JVM इसे एकत्र करने में असमर्थ है। यह दूसरी स्निपेट में होता है। OOME, एक नई लॉन्ग क्रिएशन द्वारा ट्रिगर किया गया है, पकड़ा गया है, लेकिन जल्द ही हम एक नया ऑब्जेक्ट ( System.out,printlnलाइन में एक स्ट्रिंग ) बना रहे हैं, और ढेर लगभग पूरा हो गया है, इसलिए एक नया OOME फेंक दिया गया है। यह सबसे खराब स्थिति है: हमने एक नई वस्तु बनाने की कोशिश की, हम विफल रहे, हमने ओओएम को पकड़ लिया, हां, लेकिन अब नए हीप मेमोरी (जैसे: एक नई वस्तु बनाने) की आवश्यकता वाले पहले निर्देश एक नया ओओएम फेंक देंगे। इसके बारे में सोचें, हम इस बिंदु पर और क्या कर सकते हैं कि इतनी कम मेमोरी बची है? शायद बस बाहर निकल रहा है। इसलिए बेकार है।

जेवीएम के संसाधनों को इकट्ठा न करने के कारणों में, एक वास्तव में डरावना है: अन्य थ्रेड्स के साथ एक साझा संसाधन भी इसका उपयोग कर रहा है। मस्तिष्क वाला कोई भी व्यक्ति देख सकता है कि अगर किसी भी तरह के गैर-प्रायोगिक ऐप पर डाला जाए तो ओओएम कितना खतरनाक हो सकता है।

मैं एक Windows x86 32bit JVM (JRE6) का उपयोग कर रहा हूं। प्रत्येक जावा ऐप के लिए डिफ़ॉल्ट मेमोरी 64 एमबी है।


यदि मैं ll=nullपकड़ ब्लॉक के अंदर क्या करूं ?
नानवनल्ला

3

एकमात्र कारण मैं सोच सकता हूं कि ओओएम त्रुटियों को पकड़ना क्यों हो सकता है कि आपके पास कुछ विशाल डेटा संरचनाएं हैं जिनका आप अब उपयोग नहीं कर रहे हैं, और कुछ मेमोरी को शून्य और खाली करने के लिए सेट कर सकते हैं। लेकिन (1) इसका मतलब है कि आप स्मृति को बर्बाद कर रहे हैं, और आपको अपने कोड को केवल एक ओमो के बाद सीमित करने के बजाय ठीक करना चाहिए, और (2) यहां तक ​​कि अगर आपने इसे पकड़ा, तो आप क्या करेंगे? OOM किसी भी समय हो सकता है, संभावित रूप से सब कुछ आधा किया जा सकता है।


3

प्रश्न 2 के लिए मैं पहले से ही वह समाधान देख रहा हूं जो मैं सुझाव दूंगा, BalusC द्वारा।

  1. क्या java.lang.OutOfMemoryError को पकड़ने पर कोई वास्तविक शब्द परिदृश्य अच्छा विचार हो सकता है?

मुझे लगता है कि मैं सिर्फ एक अच्छे उदाहरण के साथ आया हूं। जब awt एप्लिकेशन संदेशों को भेज रहा है तो अनियंत्रित OutOfMemoryError को stderr पर प्रदर्शित किया जाता है और वर्तमान संदेश का प्रसंस्करण रोक दिया जाता है। लेकिन आवेदन तो चलता रहता है! उपयोगकर्ता अभी भी दृश्य के पीछे हो रही गंभीर समस्याओं से अनजान अन्य आदेश जारी कर सकते हैं। खासकर जब वह मानक त्रुटि का पालन नहीं कर सकता है या नहीं करता है। इसलिए ओओएम अपवाद को पकड़ना और (या कम से कम सुझाव देना) एप्लिकेशन पुनरारंभ कुछ वांछित है।


3

मेरे पास बस एक परिदृश्य है जहां एक OutOfMemoryError को पकड़ना समझ में आता है और काम करने लगता है।

परिदृश्य: एक एंड्रॉइड ऐप में, मैं उच्चतम संभव रिज़ॉल्यूशन में कई बिटमैप प्रदर्शित करना चाहता हूं, और मैं उन्हें धाराप्रवाह ज़ूम करने में सक्षम होना चाहता हूं।

धाराप्रवाह जूमिंग के कारण, मैं बिटमैप को स्मृति में रखना चाहता हूं। हालांकि, एंड्रॉइड की मेमोरी में सीमाएं हैं जो डिवाइस पर निर्भर हैं और जिन्हें नियंत्रित करना मुश्किल है।

इस स्थिति में, बिटमैप को पढ़ते समय आउटऑफ़मैरीऑरर हो सकता है। यहां, यह मदद करता है अगर मैं इसे पकड़ता हूं और फिर कम रिज़ॉल्यूशन के साथ जारी रखता हूं।


0
  1. निर्भर करता है कि आप "अच्छे" को कैसे परिभाषित करते हैं। हम अपने छोटी गाड़ी के वेब एप्लिकेशन में ऐसा करते हैं और यह ज्यादातर समय काम करता है (शुक्र है, अब OutOfMemoryएक असंबंधित फिक्स के कारण ऐसा नहीं होता है)। हालाँकि, यदि आप इसे पकड़ लेते हैं, तब भी यह कुछ महत्वपूर्ण कोड को तोड़ सकता है: यदि आपके पास कई थ्रेड हैं, तो मेमोरी आवंटन उनमें से किसी में भी विफल हो सकता है। तो, आपके आवेदन के आधार पर अभी भी 10--90% संभावना है कि यह अपरिवर्तनीय रूप से टूट गया है।
  2. जहां तक ​​मैं समझता हूं, रास्ते में भारी स्टैक अन्डोइंग इतने सारे संदर्भों को अमान्य कर देगा और इस तरह से इतनी अधिक स्मृति मुक्त होगी कि आपको इसकी परवाह नहीं करनी चाहिए।

संपादित करें: मेरा सुझाव है कि आप इसे आज़माएँ। कहो, एक प्रोग्राम लिखो जो एक फ़ंक्शन को कॉल करता है जो उत्तरोत्तर अधिक मेमोरी आवंटित करता है। पकड़ें OutOfMemoryErrorऔर देखें कि क्या आप सार्थक रूप से उस बिंदु से जारी रख सकते हैं। मेरे अनुभव के अनुसार, आप कर पाएंगे, हालांकि मेरे मामले में यह वेबलॉजिक सर्वर के तहत हुआ था, इसलिए इसमें कुछ काले रंग शामिल हो सकते थे।


-1

थ्रोएबल के तहत आप कुछ भी पकड़ सकते हैं, आम तौर पर बोलते हुए आपको केवल रनटाइम एक्सेप्शन को छोड़कर अपवाद के उपवर्गों को पकड़ना चाहिए (हालांकि डेवलपर्स का एक बड़ा हिस्सा भी रनटाइम एक्ससेप्शन को पकड़ सकता है ... लेकिन वह भाषा डिजाइनरों का इरादा कभी नहीं था)।

यदि आप OutOfMemoryError को पकड़ लेते तो पृथ्वी पर क्या करते? VM मेमोरी से बाहर है, मूल रूप से आप जो कुछ भी कर सकते हैं वह सभी निकास है। आप शायद यह बताने के लिए कि आप स्मृति से बाहर हैं, उन्हें यह बताने के लिए एक संवाद बॉक्स नहीं खोल सकते हैं :-)

जब यह वास्तव में मेमोरी से बाहर हो जाता है तो VM एक OutOfMemoryError को फेंक देता है (वास्तव में सभी त्रुटियों को अपरिवर्तनीय स्थितियों का संकेत देना चाहिए) और वास्तव में ऐसा कुछ भी नहीं होना चाहिए जो आप इससे निपटने के लिए कर सकते हैं।

करने वाली चीजें यह पता लगाती हैं कि आप मेमोरी से बाहर क्यों चल रहे हैं (एक नेटाइलर की तरह एक प्रोफाइलर का उपयोग करें) और सुनिश्चित करें कि आपके पास मेमोरी लीक नहीं है। यदि आपके पास मेमोरी लीक नहीं है, तो उस मेमोरी को बढ़ाएं जिसे आपने VM को आवंटित किया है।


7
आपकी पोस्ट के प्रति गलत धारणा यह है कि OOM इंगित करता है कि JVM मेमोरी से बाहर है। इसके बजाय, यह वास्तव में इंगित करता है कि जेवीएम को उन सभी मेमोरी को आवंटित नहीं किया जा सकता है जिन्हें यह निर्देश दिया गया था। यही है, अगर JVM में 10B स्पेस है और आप 100B ऑब्जेक्ट को 'न्यू अप' करते हैं, तो यह विफल हो जाएगा, लेकिन आप 5B ऑब्जेक्ट को 'न्यू अप' कर सकते हैं।
टिम बेंडर

1
और अगर मुझे केवल 5 बी की जरूरत है तो मैं 10 बी के लिए क्यों कह रहा हूं? यदि आप परीक्षण और त्रुटि के आधार पर आवंटन कर रहे हैं तो आप इसे गलत कर रहे हैं।
टोफूबीर

2
मुझे लगता है कि टिम का मतलब था कि आप अभी भी आउटऑफमेरी की स्थिति के साथ भी कुछ काम कर सकते हैं। उदाहरण के लिए, डायलॉग खोलने के लिए पर्याप्त मेमोरी हो सकती है।
स्टीफन आइलर्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.