2010-11-22 22 views
11

ऐसा क्यों करता है:नेट OutOfMemoryException

class OutOfMemoryTest02 
{ 
    static void Main() 
    { 
     string value = new string('a', int.MaxValue); 
    } 
} 

अपवाद फेंक; लेकिन यह नहीं होगा:

class OutOfMemoryTest 
{ 
    private static void Main() 
    { 
     Int64 i = 0; 
     ArrayList l = new ArrayList(); 
     while (true) 
     { 
      l.Add(new String('c', 1024)); 

      i++; 
     } 
    } 
} 

क्या अंतर है?

+2

दूसरा क्या करता है? – SLaks

+0

दूसरा एक जेस्ट तब तक चलता रहता है जब तक मेरी मशीन प्रतिक्रिया न दे और मुझे इसे बूट करना पड़ेगा – ksm

+0

यह इस पोस्ट के भविष्य के आगंतुकों के लिए ध्यान देने योग्य हो सकता है .net 4.5 अगर मैं इसे सही तरीके से पढ़ रहा हूं तो इस सीमा को हटा दें। http://msdn.microsoft.com/en-us/library/hh285054(v=vs.110).aspx – TravisWhidden

उत्तर

9

क्या आपने दस्तावेज़ों में int.MaxValue देखा है? यह 2 जीबी के समतुल्य है, जो कि 'ए' वर्णों के एक संगत ब्लॉक के लिए संभवतः अधिक रैम है - यही वह है जिसे आप यहां पूछ रहे हैं।

http://msdn.microsoft.com/en-us/library/system.int32.maxvalue.aspx

आपका अनंत लूप अंत में एक ही अपवाद (या एक अलग एक परोक्ष रूप से राम के अति प्रयोग से संबंधित) का कारण होगा, लेकिन यह कुछ समय ले जाएगा। लूप केस में लक्षण तेजी से पुन: उत्पन्न करने के लिए 1024 से 10 * 1024 * 1024 पर बढ़ने का प्रयास करें।

जब मैं इस बड़े स्ट्रिंग आकार के साथ चलता हूं, मुझे 68 लूप के बाद 10 सेकंड के अंदर अपवाद मिलता है (i जांचना)। क्योंकि नेट के string लंबाई सीमित है

+0

yup। मैं उस तथ्य को समझता हूं। मैं जावा दुनिया से आया हूं, अगर आवंटित करने के लिए कोई और सिस्टम मेमोरी उपलब्ध नहीं है तो वीएम चिल्लाएगा। लेकिन .NET में, विशेष रूप से दूसरा उदाहरण ... मैं वर्चुअल रूप से सिस्टम को एक गैर-उत्तरदायी स्थिति में ला सकता हूं और वीएम कभी शोर नहीं करता ... उसके साथ क्या चल रहा है? – ksm

+0

तो, नेट में आपको इसके बजाय 'OutOfMemoryException' मिलेगा। JAVA में –

+0

मैं अंतहीन सूची में एक नई स्ट्रिंग नहीं जोड़ सकता, JVM त्रुटि देता है ... या तो यह एक बहुत बड़ी स्ट्रिंग (जैसा कि 1 में है) या किसी सूची में कई छोटे स्ट्रिंग जोड़ना ... दोनों मामलों में जेवीएम त्रुटि देगा। – ksm

6

आपका

new string('a', int.MaxValue); 

फेंकता एक OutOfMemoryException बस। MSDN docs में "टिप्पणियां" अनुभाग कहता है:

स्मृति में स्ट्रिंग ऑब्जेक्ट का अधिकतम आकार 2 जीबी या लगभग 1 बिलियन वर्ण है।

अपने सिस्टम पर (.NET 4.5 64) new string('a', int.MaxValue/2 - 31) फेंकता है, जबकि new string('a', int.MaxValue/2 - 32) काम करता है।

आपके दूसरे उदाहरण में, अनंत लूप ~ 2048 बाइट ब्लॉक आवंटित करता है जब तक कि आपका ओएस आभासी पता स्थान में कोई और ब्लॉक आवंटित नहीं कर सकता। जब यह पहुंचाया जाता है, तो आपको OutOfMemoryException भी मिल जाएगा।

(~ 2048 बाइट = 1024 वर्ण * UTF-16 कोड बिंदु + स्ट्रिंग भूमि के ऊपर बाइट प्रति 2 बाइट्स)

एरिक के इस great article की कोशिश करो।

3

स्ट्रिंग ऑब्जेक्ट मेमोरी उपयोग को कम करने के लिए बैकिंग साझा स्ट्रिंग पूल का उपयोग कर सकता है। पूर्व मामले में, आप एक स्ट्रिंग उत्पन्न कर रहे हैं जो कई गीगाबाइट्स है। दूसरे मामले में, इसकी संभावना है कि संकलक स्ट्रिंग को ऑटो-इंटर्न कर रहा है, इसलिए आप 1024 बाइट स्ट्रिंग उत्पन्न कर रहे हैं, और उसके बाद उसी स्ट्रिंग को कई बार संदर्भित कर रहे हैं।

कहा जा रहा है कि उस आकार के एक ऐरेलिस्ट को आपको स्मृति से बाहर ले जाना चाहिए, लेकिन इसकी संभावना है कि आपने स्मृति को चलाने के लिए कोड को लंबे समय तक चलाने की अनुमति नहीं दी है।

+0

दरअसल, स्ट्रिंग सीटीआर साझा पूल का उपयोग नहीं करेगा। – SLaks

+0

-1 यह रनटाइम जेनरेट स्ट्रिंग है जिसे इसे इंटर्न नहीं किया जाएगा। –

+0

मैंने इसे चलाने दिया ... असल में, मैंने बाद में आवंटन के बीच किसी भी देरी के बिना प्रोग को चलाया था और मेरे कंप्यूटर ने 10 से भी कम समय में प्रतिक्रिया देना बंद कर दिया था ... – ksm

4

क्योंकि int.MaxValue 2,147,483,647 है, या 2 गीगाबाइट्स जिन्हें आवंटित करने की आवश्यकता है।

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

+1

मैंने एक (बहुत) अंधेरे जगह में समाप्त किया :) क्या वीएम _never_ मुझे बताएगा कि मैं ढेर से बाहर निकलने जा रहा हूं? मैं बस स्मृति के लिए कई छोटे चर जोड़ सकते हैं ... हमेशा के लिए? – ksm

0

एक अंधेरी जगह :) में पहुंचते हैं अपने पहले नमूने में आप एक समय

पर एक 2 जी स्ट्रिंग बनाने के लिए दूसरे उदाहरण रखने में कोशिश कर रहे हैं हूँ एक सरणी में 1k जोड़ना। उपभोग की समान राशि तक पहुंचने के लिए आपको 2 मिलियन से अधिक बार लूप करना होगा।

और यह सभी एक ही चर में एक साथ संग्रहीत नहीं है। इस प्रकार, मुझे लगता है कि नए डेटा के लिए जगह बनाने के लिए डिस्क में से कुछ मेमोरी उपयोग को डिस्क पर रखा जा सकता है।

0

क्योंकि एक वस्तु cannot have more than 2 GB:

पहले कुछ पृष्ठभूमि; नेट क्रम (CLR) के 2.0 संस्करण में हम एक सचेत डिजाइन निर्णय, अधिकतम ऑब्जेक्ट आकार 2GB पर जीसी ढेर में अनुमति दी रखने के लिए भी क्रम

के 64-बिट संस्करण में पर किए गए अपने पहला उदाहरण, आप ऑब्जेक्ट ओवरहेड (8 बाइट्स) के साथ 2 जीबी, ऑब्जेक्ट आवंटित करने का प्रयास करते हैं, यह बहुत बड़ा है।

मुझे नहीं पता कि ऐरेलीस्ट आंतरिक रूप से कैसे काम करता है, लेकिन आप मेरे ज्ञान के लिए 2 जीबी और ऐरेलिस्ट के कई ऑब्जेक्ट आवंटित करते हैं - केवल पॉइंटर्स रखता है जो 4 (x64 पर 8?) बाइट्स हैं, भले ही कितना बड़ा हो ऑब्जेक्ट वे इंगित करते हैं।

another article के शब्दों में:

इसके अलावा, वस्तुओं अन्य वस्तुओं की दुकान केवल संदर्भ के लिए संदर्भ है। तो यदि आपके पास ऐसी ऑब्जेक्ट है जो तीन अन्य ऑब्जेक्ट्स के संदर्भ रखती है, तो स्मृति पदचिह्न केवल 12 अतिरिक्त बाइट्स है: प्रत्येक संदर्भित ऑब्जेक्ट्स में से 32-बिट पॉइंटर। इससे कोई फर्क नहीं पड़ता कि संदर्भित वस्तु कितनी बड़ी है।

+0

ऐरेलिस्ट में 2 जीबी पॉइंटर्स नहीं हो सकते हैं। – SLaks

2

दूसरा स्निपेट भी क्रैश हो जाएगा। यह पूरी तरह से धीमी गति से लेता है क्योंकि यह स्मृति को धीमा कर रहा है। अपनी हार्ड डिस्क एक्सेस लाइट पर ध्यान दें, यह बेहोशी से चमक रहा है जबकि विंडोज कमरे बनाने के लिए रैम से बाहर पृष्ठों को चकित करता है। पहला स्ट्रिंग कन्स्ट्रक्टर तुरंत विफल रहता है क्योंकि हीप मैनेजर आपको 4 गीगाबाइट आवंटित करने की अनुमति नहीं देगा।

+0

या, 2 गीगाबाइट;) –

+0

अंत में, एक सही उत्तर! – SLaks

+1

@Moo: चार्स दो बाइट चौड़े हैं। – SLaks

1

दोनों संस्करण ओओएम अपवाद का कारण बनेंगे, यह सिर्फ (32 बिट मशीन पर) आप इसे "एकल" बहुत बड़ी वस्तु आवंटित करने का प्रयास करते समय तुरंत संस्करण के साथ प्राप्त करेंगे।

दूसरे संस्करण बहुत लंबे समय तक हालांकि ताड़ना का एक बहुत ले जाएगा के रूप में वहाँ हो जाएगा कारकों की एक जोड़ी के लिए यह OOM हालत को पाने के लिए:

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

  • जीसी के प्रयासों और मुफ्त मेमोरी के लिए पीढ़ियों में लाखों वस्तुओं को स्कैन करने के रूप में पेज थ्रैशिंग होगा। स्कैनिंग में बड़ी मात्रा में स्मृति को लगातार और बाहर पगड़ने का कारण बनता है।

ताड़ना प्रणाली एक पड़ाव प्रसंस्करण भूमि के ऊपर करने के लिए पीसने के लिए और इतने OOM हालत एक लंबा समय लगेगा पहुँचा जा का कारण होगा। अधिकांश समय जीसी पर थ्रैशिंग और दूसरे संस्करण के लिए पेजिंग खर्च किया जाएगा।

0

एक कारण यह है कि आपका सिस्टम रुकावट में आ सकता है क्योंकि .NET का कोड धातु के करीब आता है और आप एक तंग लूप में हैं जो 100% सीपीयू का उपभोग करेगी, बशर्ते प्रोसेस प्राथमिकता इसे अनुमति दे। यदि आप कसकर लूप करने के दौरान एप्लिकेशन को बहुत अधिक CPU का उपभोग करने से रोकना चाहते हैं तो आपको लूप के अंत में System.Threading.Thread.Sleep(10) जैसे कुछ जोड़ना चाहिए, जो जबरन अन्य थ्रेडों को प्रोसेसिंग समय प्रदान करेगा।

जेवीएम और .NET के सीएलआर (कॉमन लैंग्वेज रनटाइम) के बीच एक बड़ा अंतर यह है कि सीएलआर आपकी स्मृति के आकार को x64 सिस्टम/एप्लिकेशन (32 बिट अनुप्रयोगों में, बड़े पते के बारे में ओएस ध्वज के बिना सीमित नहीं करता है संबोधित सीमाओं के कारण 2 जीबी तक किसी भी आवेदन को सीमित करता है)। जेआईटी कंपाइलर आपके प्रोसेसिंग आर्किटेक्चर के लिए देशी विंडोज कोड बनाता है और फिर इसे उसी दायरे में चलाता है कि कोई भी अन्य विंडोज़ एप्लीकेशन चलाएगा। JVM एक अलग पृथक सैंडबॉक्स है जो कॉन्फ़िगरेशन/कमांड लाइन स्विच के आधार पर एप्लिकेशन को निर्दिष्ट आकार में बाध्य करता है।

दो एल्गोरिदम के बीच मतभेद का सवाल है: जब पर्याप्त सन्निहित स्मृति के साथ एक 64 वातावरण में चल रहा 4GB int.MaxValue वर्ण हो करने के लिए आवश्यक आवंटित करने के लिए

एकल स्ट्रिंग निर्माण इसकी गारंटी नहीं है विफल (.NET तार डिफ़ॉल्ट रूप से यूनिकोड हैं, जिसके लिए प्रति अक्षर 2 बाइट्स की आवश्यकता होती है)। एक बड़ा 32 पता एप्लिकेशन हमेशा विफल रहता है, यहां तक ​​कि बड़े पते के पता ध्वज सेट के साथ भी, क्योंकि अधिकतम मेमोरी अभी भी 3.5 जीबी की तरह है)।

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

संपादित करें:

:

इन दो लेख एक छोटे से बड़े हैं, लेकिन गहराई पढ़ने में बहुत अच्छा कर रहे हैं:

सोचा मैं कुछ लिंक लोग नेट स्मृति को समझने के लिए काम मिल सकता है देना चाहते हैं Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework

Garbage Collection Part 2: Automatic Memory Management in the Microsoft .NET Framework

ये एक .NET कूड़ा संग्रह से विकसित ब्लॉग हैं एर नेट स्मृति प्रबंधन के नए संस्करण के बारे में जानकारी के लिए:

So, what’s new in the CLR 4.0 GC?

CLR 4.5: Maoni Stephens - Server Background GC

यह तो सवाल यह आप की अंदरूनी कामकाज का निरीक्षण करने में मदद कर सकते हैं।नेट मेमोरी:

.NET Memory Profiling Tools