2008-12-01 7 views
7

मैं हाल ही में कुछ कोड में यह भर में आया था - मूल रूप से किसी को एक बड़ी वस्तु बनाने के लिए, मुकाबला, जब पर्याप्त ढेर नहीं है इसे बनाने की कोशिश कर रहा:जावा: ऑब्जेक्ट बनाने के लिए पर्याप्त मुफ्त ढेर?

try { 
    // try to perform an operation using a huge in-memory array 
    byte[] massiveArray = new byte[BIG_NUMBER]; 
} 
catch (OutOfMemoryError oome) { 
    // perform the operation in some slower but less 
    // memory intensive way... 
} 

यह सही नहीं लगता है, सूर्य के बाद से खुद को सुझाव है कि आप Error या इसके उप-वर्गों को पकड़ने की कोशिश नहीं करनी चाहिए। हम यह चर्चा की, और एक अन्य विचार है कि आया था स्पष्ट रूप से मुक्त करने के ढेर के लिए जाँच किया गया था:

if (Runtime.getRuntime().freeMemory() > SOME_MEMORY) { 
    // quick memory-intensive approach 
} 
else { 
    // slower, less demanding approach 
} 

फिर, यह असंतोषजनक लगता है - विशेष रूप से है कि उठा SOME_MEMORY के लिए एक मूल्य आसानी से प्रश्न में नौकरी से संबंधित के लिए मुश्किल है: के लिए कुछ मनमानी बड़ी वस्तु, मैं अनुमान लगा सकता हूं कि इसकी तत्कालता की कितनी मेमोरी की आवश्यकता हो सकती है?

क्या ऐसा करने का कोई बेहतर तरीका है? क्या यह जावा में भी संभव है, या भाषा के अमूर्त स्तर के नीचे स्मृति का प्रबंधन करने का कोई विचार है?

संपादित करें 1: पहले उदाहरण में, यह वास्तव में स्मृति एक दिया लंबाई हासिल हो सकता है की एक byte[] राशि का अनुमान लगाने के लिए संभव हो सकता है, लेकिन वहाँ एक अधिक सामान्य तरीका है कि मनमाने ढंग से बड़ी वस्तुओं तक फैली हुई है?

संपादित करें 2: रूप @erickson बताते हैं, वहाँ एक वस्तु के आकार का अनुमान एक बार यह बनाया तरीके हैं, लेकिन (पिछले वस्तु आकार के आधार पर एक सांख्यिकीय दृष्टिकोण अनदेखी) वहाँ अभी तक के लिए ऐसा करने का एक तरीका है -शिक्षित वस्तुओं?

वहाँ

के रूप में भी है कि क्या यह OutOfMemoryError को पकड़ने के लिए उचित है के लिए कुछ बहस हो रहा है - किसी को भी कुछ भी निर्णायक पता है?

उत्तर

5

फ्रीमेमरी बिल्कुल सही नहीं है। आपको maxMemory() - कुल मेमरी() जोड़ना होगा। जैसे मानते हुए कि आप अधिकतम-मेमोरी = 100 एम के साथ वीएम शुरू करते हैं, JVM आपके विधि कॉल के समय केवल (ओएस से) 50 एम का उपयोग कर सकता है। उसमें, मान लीजिए कि 30 एम वास्तव में जेवीएम द्वारा उपयोग में है। इसका मतलब है कि आप 20 एम मुक्त दिखाएंगे (मोटे तौर पर, क्योंकि हम केवल ढेर के बारे में बात कर रहे हैं), लेकिन यदि आप अपनी बड़ी वस्तु बनाने की कोशिश करते हैं, तो यह अन्य 50 एम को पकड़ने का प्रयास करेगा, इसका अनुबंध इसे लेने की अनुमति देता है छोड़ने और त्रुटि देने से पहले ओएस। तो आप वास्तव में (सैद्धांतिक रूप से) 70 एम उपलब्ध होगा।

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

आप क्योंकि

-यह गारंटी नहीं है तुरंत

-यह होगा चलाने के लिए, मैन्युअल रूप से एक System.GC ट्रिगर द्वारा इस बिट के आसपास पाने के लिए, सिवाय इसके कि इस तरह के एक बहुत अच्छी बात करने के लिए नहीं है कि कोशिश कर सकते हैं

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

+0

System.gc() आमतौर पर सबकुछ बंद नहीं करता है। यह अंतर्निहित कचरा कलेक्टर पर निर्भर करता है। – erickson

2

मुझे विश्वास नहीं है कि इसके लिए एक उचित, सामान्य दृष्टिकोण है जो सुरक्षित रूप से 100% विश्वसनीय माना जा सकता है। यहां तक ​​कि Runtime.freeMemory दृष्टिकोण इस तथ्य के लिए कमजोर है कि कचरा संग्रह के बाद आपके पास वास्तव में पर्याप्त स्मृति हो सकती है, लेकिन आप तब तक नहीं जान पाएंगे जब तक आप एक जीसी को मजबूर नहीं करते। लेकिन फिर जीसी को मजबूर करने के लिए कोई मूर्ख तरीका नहीं है। :)

यह कहकर, मुझे संदेह है कि आपको वास्तव में कितना पता था कि आपने कितनी जरूरत है, और पहले से ही System.gc() चलाया है, और आपके एक साधारण सिंगल-थ्रेडेड ऐप में चल रहा है, तो आपके पास उचित मेमरी कॉल के साथ इसे सही तरीके से प्राप्त करने के लिए उचित रूप से सभ्य शॉट।

तो उन बाधाओं के किसी भी असफल है, हालांकि, और आप यह OOM त्रुटि, एक वर्ग पर अपनी पीठ मिलता है, और इसलिए शायद कोई बस त्रुटि उपवर्ग को पकड़ने की तुलना में बेहतर कर रहे हैं। हालांकि इसके साथ जुड़े कुछ जोखिम हैं (सूर्य के वीएम ओओएम के बाद क्या होता है इसके बारे में बहुत सारी गारंटी नहीं देता है ... आंतरिक राज्य भ्रष्टाचार का कुछ जोखिम है), ऐसे कई ऐप्स हैं जिनके लिए बस इसे पकड़ना और जीवन के साथ आगे बढ़ना आपको कोई गंभीर नुकसान नहीं पहुंचाएगा।

एक और अधिक दिलचस्प सवाल मेरे मन में, हालांकि, क्यों मामलों में जहां आप ऐसा करने के लिए पर्याप्त स्मृति और दूसरों की क्या ज़रूरत है, जहां तुम नहीं देखते हैं क्या है? शायद प्रदर्शन व्यापार के कुछ और विश्लेषण शामिल असली जवाब है?

+0

आपने सिर पर वास्तविक जीवन समस्या को मारा है - वास्तविक फ़िक्स विधि को फिर से लिखना था ताकि इसे शुरू करने के लिए बहुत सारी मेमोरी की आवश्यकता न हो। सवाल अधिक अकादमिक था, लेकिन अभी भी दिलचस्प ... –

+0

@jsight "क्यों मामले हैं जहां .." आपके बारे में क्या है (और मैं यह नहीं कह रहा हूं कि यह स्मृति में किया जाना चाहिए) फ़ाइल सिस्टम फ़ाइल एन्क्रिप्ट करना। कभी-कभी पूरी फाइल 1 एमबी है, अन्य 12 जीबी है। ? ऐसा होने पर पर्याप्त रैम जारी करने का कोई तरीका नहीं है !! – OscarRyz

+0

@ ऑस्कर: मुझे नहीं लगता कि मैं उन दो मामलों से निपटने का तरीका निर्धारित करने के लिए मुफ्त मेमोरी का पता लगाने की कोशिश करूंगा।मैं रीयल-टाइम टेलीमेट्री की बजाय स्टार्टअप पर sys मेमोरी के आधार पर निर्धारित ब्लॉक आकार के साथ फ़ाइल आकार पर अपने निर्णय को आधार देने की अधिक संभावना रखूंगा। – jsight

2

निश्चित रूप से त्रुटि को पकड़ना सबसे खराब दृष्टिकोण है। त्रुटि तब होती है जब कुछ भी नहीं है आप इसके बारे में कर सकते हैं। एक लॉग, पफ भी नहीं बनाते, जैसे "... ह्यूस्टन, हमने वीएम खो दिया"।

मुझे दूसरा कारण नहीं मिला। यह बुरा था क्योंकि ऑपरेशन में SOME_MEMORY को जोड़ना मुश्किल है? क्या आप इसे मेरे लिए दोबारा कर सकते हैं?

एकमात्र विकल्प मैं देख रहा हूँ, स्मृति (पुराने दिनों में के रूप में राम/ROM) के रूप में हार्ड डिस्क का उपयोग करने के मुझे लगता है कि आप अपने "और धीमी, कम मांग दृष्टिकोण"

में इंगित कर रहे है

हर मंच इसकी सीमाओं, जावा suppport जितना राम अपने हार्डवेयर देने के लिए तैयार है (अच्छी तरह से आप वास्तव में वी एम कॉन्फ़िगर करके) सूर्य JVM impl कि

-Xmx 

विकल्प

के साथ किया जा सकता है में

जैसे

java -Xmx8g some.name.YourMemConsumingApp 

उदाहरण

बेशक आप एक ऑपरेशन रैम

है कि आपके मामला है 10 जीबी लेता है निष्पादित करने का प्रयास हो सकती के

के लिए तो आप निश्चित रूप से डिस्क के लिए स्वैप करना चाहिए।

इसके अतिरिक्त, रणनीति पैटर्न का उपयोग करके एक अच्छा कोड बना सकता है। यहाँ हालांकि यह overkill दिखता है:

if(isEnoughMemory(SOME_MEMORY)){ 
    strategy = new InMemoryStrategy(); 
}else{ 
    strategy = new DiskStrategy(); 
} 

strategy.performTheAction(); 

लेकिन यह मदद मिल सकती है, तो "और" कोड का एक बहुत कुछ शामिल है और बुरा लग रहा है। दूसरे के साथ समस्या यह प्राप्त करने के बाद पी

संपादित

: इसके अलावा अगर किसी भी तरह आप (प्रसंस्करण के लिए एक बादल का उपयोग कर की तरह) एक तिहाई दृष्टिकोण का उपयोग कर सकते हैं यदि आप किसी तृतीय रणनीति

... 
strategy = new ImaginaryCloudComputingStrategy(); 
... 

जोड़ सकते हैं दृष्टिकोण: यदि कुछ समय होते हैं जब आप नहीं जानते कि कितनी रैम खपत की जा रही है लेकिन आप जानते हैं कि आपने कितना छोड़ा है, तो आप एक मिश्रित दृष्टिकोण का उपयोग कर सकते हैं (जब आपके पास पर्याप्त होता है, तो राम [डिस्क] जब आप नहीं)

मान लीजिए कि यह सैद्धांतिक समस्या है।

मान लीजिए कि आपको स्ट्रीम से एक फ़ाइल प्राप्त होती है और यह नहीं पता कि यह कितना बड़ा है।

फिर आप उस स्ट्रीम पर कुछ ऑपरेशन करते हैं (उदाहरण के लिए इसे एन्क्रिप्ट करें)।

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

जब स्मृति से बाहर चल रहे वी एम जीसी करेंगे, तो आप और अधिक स्मृति और फिर आप अन्य हिस्सा प्रदर्शन करते हैं। और यह तब तक दोहराया जाता है जब तक आपके पास बड़ी स्ट्रीम संसाधित नहीं होती है।

while(!isDone()) { 
     if(isMemoryLow()){ //Runtime.getRuntime().freeMemory() < SOME_MEMORY + some other validations 
      swapToDisk(); // and make sure resources are GC'able 
     } 
     byte [] array new byte[PREDEFINED_BUFFER_SIZE]; 
     process(array); 

     process(array); 
} 
cleanUp(); 
+0

रिवार्ड - सलाह के लिए धन्यवाद। क्या यह कोई स्पष्ट है? –

+0

हाँ, निश्चित रूप से धन्यवाद। – OscarRyz

2

कुछ kludges that you can use to estimate the size of an existing object हैं; आप इनमें से कुछ को अभी तक बनाए गए ऑब्जेक्ट के आकार की भविष्यवाणी करने के लिए अनुकूलित कर सकते हैं।

हालांकि, इस मामले में, मुझे लगता है कि त्रुटि को पकड़ना सबसे अच्छा हो सकता है। सबसे पहले, मुफ्त मेमोरी के लिए पूछना कचरा संग्रह के बाद उपलब्ध नहीं है, जो ओओएमई बढ़ाने से पहले किया जाएगा। और, System.gc() के साथ एक कचरा संग्रह का अनुरोध विश्वसनीय नहीं है। यह अक्सर स्पष्ट रूप से अक्षम होता है क्योंकि यह प्रदर्शन को तोड़ सकता है, और यदि यह अक्षम नहीं है और नरक है; ठीक है, जब अनावश्यक रूप से उपयोग किया जाता है तो यह प्रदर्शन को तोड़ सकता है।

अधिकांश त्रुटियों से पुनर्प्राप्त करना असंभव है। हालांकि, वसूली योग्यता कॉलर तक है, कैली नहीं। इस मामले में, यदि आपके पास OutOfMemoryError से पुनर्प्राप्त करने की कोई रणनीति है, तो इसे पकड़ने और वापस गिरने के लिए मान्य है।

मुझे लगता है कि, व्यावहारिक रूप से, यह वास्तव में "धीमी" और "तेज़" तरीके के बीच के अंतर के लिए आता है। यदि "धीमी" विधि पर्याप्त तेज़ है, तो मैं इसके साथ रहूंगा, क्योंकि यह सुरक्षित और सरल है। और, ऐसा लगता है, इसे एक पतन के रूप में इस्तेमाल करने की इजाजत देने का मतलब है कि यह "पर्याप्त तेज़" है। छोटे अनुकूलन को अपने आवेदन की विश्वसनीयता को कम न करने दें।

2

"त्रुटि आवंटित करने और संभालने का प्रयास करें" दृष्टिकोण बहुत खतरनाक है।

  • क्या होगा यदि आपको मुश्किल से आपकी याद आती है? बाद में ओओएम अपवाद हो सकता है क्योंकि आप चीजों को बहुत करीब लाते हैं। लगभग किसी पुस्तकालय कॉल कम से कम संक्षेप में स्मृति आवंटित करेगा।
  • आपके आवंटन के दौरान अपेक्षाकृत छोटी वस्तु आवंटित करने का प्रयास करते समय एक अलग थ्रेड ओओएम अपवाद प्राप्त कर सकता है। भले ही आपका आवंटन विफल होने के लिए नियत है।

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

संबंधित मुद्दे