'System.OutOfMemoryException' को तब फेंका गया था जब अभी भी बहुत सारी मेमोरी फ्री है


92

यह मेरा कोड है:

int size = 100000000;
double sizeInMegabytes = (size * 8.0) / 1024.0 / 1024.0; //762 mb
double[] randomNumbers = new double[size];

अपवाद: प्रकार के अपवाद 'System.OutOfMemoryException' को फेंक दिया गया था।

इस मशीन पर मेरे पास 4GB मेमोरी है। 2.5 जीबी मुफ्त है जब मैं इसे चालू कर रहा हूं, तो 100000000 यादृच्छिक संख्याओं के 762mb को संभालने के लिए पीसी पर स्पष्ट रूप से पर्याप्त जगह है। मुझे उपलब्ध स्मृति के रूप में कई यादृच्छिक संख्याओं को संग्रहीत करने की आवश्यकता है। जब मैं प्रोडक्शन में जाऊंगा तो बॉक्स पर 12GB होगा और मैं इसका उपयोग करना चाहता हूं।

क्या CLR मुझे शुरू करने के लिए एक डिफ़ॉल्ट अधिकतम मेमोरी के लिए विवश करता है? और मैं और अधिक कैसे अनुरोध करूं?

अपडेट करें

मैंने सोचा कि इसे छोटे-छोटे विखंडों में तोड़ना और मेरी स्मृति आवश्यकताओं में वृद्धि करना अगर स्मृति विखंडन के कारण समस्या है, तो इससे मदद मिलेगी , लेकिन यह नहीं है कि मैं 256mb के कुल ArrayList आकार को अतीत में नहीं पा सकता हूं, भले ही मैं इसे ब्लॉक कर रहा हूं

private static IRandomGenerator rnd = new MersenneTwister();
private static IDistribution dist = new DiscreteNormalDistribution(1048576);
private static List<double> ndRandomNumbers = new List<double>();

private static void AddNDRandomNumbers(int numberOfRandomNumbers) {
    for (int i = 0; i < numberOfRandomNumbers; i++) {
      ndRandomNumbers.Add(dist.ICDF(rnd.nextUniform()));                
  }
}

मेरी मुख्य विधि से:

int blockSize = 1000000;

while (true) {
  try
  {
    AddNDRandomNumbers(blockSize);                    
  }
  catch (System.OutOfMemoryException ex)
  {
    break;
  }
}            
double arrayTotalSizeInMegabytes = (ndRandomNumbers.Count * 8.0) / 1024.0 / 1024.0;

6
मैं आपके आवेदन की खोज करने की सिफारिश करूंगा ताकि आपको इतनी मेमोरी का उपयोग न करना पड़े। आप क्या कर रहे हैं कि आपको एक ही बार में एक सौ मिलियन नंबरों की आवश्यकता है?
एरिक लिपर्ट

2
आपने अपने पेजफाइल या कुछ मूर्खतापूर्ण उस तरह से अक्षम नहीं किया है, क्या आपके पास है?
जुलफ

@ EricLippert, मैं P बनाम NP प्रॉब्लम ( क्लेमाथ.ऑर्ग / क्लेमेनिअम -प्रोब्लेम्स/ p-vs- np-problem ) पर काम करते समय इसे चला रहा हूं । क्या आपके पास काम करने के स्मृति उपयोग को कम करने का सुझाव है? (उदाहरण के लिए हार्ड डिस्क पर डेटा का सीरियलाइज़िंग और भंडारण करना, C ++ डेटा प्रकार आदि का उपयोग करना)
devinbost

@bosit यह एक प्रश्न और उत्तर साइट है। यदि आपके पास वास्तविक कोड के बारे में एक विशिष्ट तकनीकी प्रश्न है, तो इसे एक प्रश्न के रूप में पोस्ट करें।
एरिक लिपर्ट

आपकी टिप्पणी में P बनाम NP समस्या के लिए लिंक @bostIT अब मान्य नहीं है।
RBT

जवाबों:


140

आप इसे पढ़ना चाह सकते हैं: एरिक लिपर्ट द्वारा "" मेमोरी से बाहर " भौतिक स्मृति को संदर्भित नहीं करता है "।

संक्षेप में, और बहुत सरल, "मेमोरी से बाहर" का वास्तव में मतलब नहीं है कि उपलब्ध मेमोरी की मात्रा बहुत कम है। सबसे आम कारण यह है कि वर्तमान पते की जगह के भीतर, मेमोरी का कोई सन्निहित हिस्सा नहीं है जो कि वांछित आवंटन को पूरा करने के लिए पर्याप्त है। यदि आपके पास 100 ब्लॉक हैं, प्रत्येक 4 एमबी बड़ा है, तो आपको 5 एमबी ब्लॉक की आवश्यकता होने पर आपकी मदद नहीं होगी।

प्रमुख बिंदु:

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

"यदि आपके पास 100 ब्लॉक हैं, तो प्रत्येक 4 एमबी बड़ा, जो आपको 5 एमबी ब्लॉक की आवश्यकता होने पर आपकी मदद करने वाला नहीं है" - मुझे लगता है कि यह एक छोटे से सुधार के साथ बेहतर होगा: "यदि आपके पास 100 " ब्लॉक "ब्लॉक हैं"
13

31

जांचें कि आप 64-बिट प्रक्रिया का निर्माण कर रहे हैं, न कि 32-बिट वाला, जो विज़ुअल स्टूडियो का डिफ़ॉल्ट संकलन मोड है। ऐसा करने के लिए, अपने प्रोजेक्ट, गुण -> बिल्ड -> प्लेटफ़ॉर्म लक्ष्य: x64 पर राइट क्लिक करें। किसी भी 32-बिट प्रक्रिया के रूप में, 32-बिट में संकलित विज़ुअल स्टूडियो अनुप्रयोगों में 2GB की वर्चुअल मेमोरी सीमा होती है।

64-बिट प्रक्रियाओं में यह सीमा नहीं है, क्योंकि वे 64-बिट पॉइंटर्स का उपयोग करते हैं, इसलिए उनका सैद्धांतिक अधिकतम पता स्थान (उनकी वर्चुअल मेमोरी का आकार) 16 एक्साबाइट्स (2 ^ 64) है। वास्तव में, विंडोज x64 प्रक्रियाओं की आभासी मेमोरी को 8TB तक सीमित करता है। मेमोरी सीमा समस्या का समाधान 64-बिट में संकलित करना है।

हालाँकि, Visual Studio में ऑब्जेक्ट का आकार अभी भी डिफ़ॉल्ट रूप से 2GB तक सीमित है। आप कई सरणियाँ बना पाएंगे जिनका संयुक्त आकार 2GB से अधिक होगा, लेकिन आप डिफ़ॉल्ट रूप से 2GB से बड़ा सरणियाँ नहीं बना सकते हैं। उम्मीद है, अगर आप अभी भी 2GB से बड़े सरणियों को बनाना चाहते हैं, तो आप निम्न कोड को app.config फ़ाइल में जोड़कर कर सकते हैं:

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>

तुमने मुझे बचाया। धन्यवाद!
ती ज़ाद अक्क

25

आपके पास 762MB आवंटित करने के लिए आपके पास मेमोरी का एक निरंतर ब्लॉक नहीं है, आपकी मेमोरी खंडित है और आवंटनकर्ता को आवश्यक मेमोरी आवंटित करने के लिए एक बड़ा पर्याप्त छेद नहीं मिल सकता है।

  1. आप / 3 जीबी के साथ काम करने की कोशिश कर सकते हैं (जैसा कि अन्य ने सुझाव दिया था)
  2. या 64 बिट ओएस पर स्विच करें।
  3. या एल्गोरिथ्म को संशोधित करें ताकि इसे मेमोरी के एक बड़े हिस्से की आवश्यकता न हो। शायद स्मृति के कुछ छोटे (अपेक्षाकृत) भाग आवंटित करें।

7

जैसा कि आप शायद समझ गए हैं, मुद्दा यह है कि आप स्मृति के एक बड़े सन्निहित ब्लॉक को आवंटित करने की कोशिश कर रहे हैं, जो स्मृति विखंडन के कारण काम नहीं करता है। यदि मुझे वह करने की आवश्यकता है जो आप कर रहे हैं तो मैं निम्नलिखित कार्य करूंगा:

int sizeA = 10000,
    sizeB = 10000;
double sizeInMegabytes = (sizeA * sizeB * 8.0) / 1024.0 / 1024.0; //762 mb
double[][] randomNumbers = new double[sizeA][];
for (int i = 0; i < randomNumbers.Length; i++)
{
    randomNumbers[i] = new double[sizeB];
}

फिर, एक विशेष सूचकांक प्राप्त करने के लिए जिसका आप उपयोग करेंगे randomNumbers[i / sizeB][i % sizeB]

एक अन्य विकल्प यदि आप हमेशा मूल्यों को एक्सेस करते हैं, तो बीज को निर्दिष्ट करने के लिए अतिभारित कंस्ट्रक्टर का उपयोग किया जा सकता है । इस तरह आपको एक अर्ध यादृच्छिक संख्या प्राप्त होगी (जैसे DateTime.Now.Ticks) इसे एक चर में संग्रहीत करें, फिर जब आप सूची से गुजरना शुरू करेंगे तो आप मूल बीज का उपयोग करके एक नया यादृच्छिक उदाहरण बनाएंगे:

private static int randSeed = (int)DateTime.Now.Ticks;  //Must stay the same unless you want to get different random numbers.
private static Random GetNewRandomIterator()
{
    return new Random(randSeed);
}

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

2GB की सीमा का मतलब randomNumbers 2GB से कम होना चाहिए। चूंकि सरणियाँ कक्षाएं हैं और कुछ ओवरहेड हैं, इसलिए वे इसका मतलब है कि एक सरणी को double2 ^ 31 से छोटा होना होगा। मुझे यकीन नहीं है कि तब 2 ^ 31 की लंबाई कितनी छोटी होगी, लेकिन एक .NET सरणी का ओवरहेड? 12 - 16 बाइट्स इंगित करता है।

मेमोरी विखंडन एचडीडी विखंडन के समान है। आपके पास 2GB पता स्थान हो सकता है, लेकिन जैसा कि आप ऑब्जेक्ट बनाते हैं और नष्ट करते हैं, मूल्यों के बीच अंतराल होगा। यदि ये अंतराल आपकी बड़ी वस्तु के लिए बहुत छोटे हैं, और अतिरिक्त स्थान का अनुरोध नहीं किया जा सकता है, तो आपको यह मिल जाएगाSystem.OutOfMemoryException। उदाहरण के लिए, यदि आप 2 मिलियन, 1024 बाइट ऑब्जेक्ट बनाते हैं, तो आप 1.9GB का उपयोग कर रहे हैं। यदि आप प्रत्येक ऑब्जेक्ट को हटाते हैं, जहां पता 3 से अधिक नहीं है, तो आप .6GB मेमोरी का उपयोग कर रहे होंगे, लेकिन यह 2024 बाइट ओपन ब्लॉक्स के साथ एड्रेस स्पेस में फैल जाएगा। यदि आपको कोई ऐसी वस्तु बनाने की आवश्यकता है जो .2GB थी तो आप इसे नहीं कर पाएंगे क्योंकि इसमें फिट होने के लिए एक बड़ा ब्लॉक नहीं है और अतिरिक्त स्थान प्राप्त नहीं किया जा सकता है (32 बिट वातावरण मानकर)। इस समस्या का संभावित समाधान छोटी वस्तुओं का उपयोग करना, स्मृति में आपके द्वारा संग्रहीत डेटा की मात्रा को कम करना या स्मृति विखंडन को रोकने / रोकने के लिए मेमोरी प्रबंधन एल्गोरिथ्म का उपयोग करने जैसी चीजें हैं। यह ध्यान दिया जाना चाहिए कि जब तक आप एक बड़े कार्यक्रम को विकसित नहीं कर रहे हैं जो बड़ी मात्रा में मेमोरी का उपयोग करता है यह एक मुद्दा नहीं होगा। इसके अलावा,

चूंकि अधिकांश प्रोग्राम ओएस से काम करने की मेमोरी का अनुरोध करते हैं और फ़ाइल मैपिंग का अनुरोध नहीं करते हैं, वे सिस्टम की रैम और पेज फ़ाइल आकार द्वारा सीमित होंगे। जैसा कि ब्लॉग पर Néstor Sánchez (Néstor Sánchez) की टिप्पणी के अनुसार C # जैसे प्रबंधित कोड के साथ आप RAM / पृष्ठ फ़ाइल सीमा और ऑपरेटिंग सिस्टम के एड्रेस स्पेस से चिपके हुए हैं।


इस तरह अब और उम्मीद की जा रही थी। उम्मीद है कि यह किसी की मदद करता है। मैंने इसे पोस्ट किया क्योंकि मैं System.OutOfMemoryException24GB RAM के साथ एक सिस्टम पर x64 प्रोग्राम चला रहा था, हालांकि मेरी सरणी केवल 2GB सामान रखने की थी।


5

मैं / 3GB विंडो बूट विकल्प के खिलाफ सलाह दूंगा। बाकी सब चीजों के अलावा (यह एक बुरी तरह से व्यवहार किए गए एप्लिकेशन के लिए ऐसा करने के लिए ओवरकिल है , और यह शायद आपकी समस्या को वैसे भी हल नहीं करेगा), यह बहुत अस्थिरता पैदा कर सकता है।

कई विंडोज ड्राइवरों को इस विकल्प के साथ परीक्षण नहीं किया गया है, इसलिए उनमें से कुछ यह मानते हैं कि उपयोगकर्ता-मोड पॉइंटर्स हमेशा पता स्थान के निचले 2GB को इंगित करते हैं। जिसका मतलब है कि वे / 3 जीबी के साथ बुरी तरह से टूट सकते हैं।

हालाँकि, Windows सामान्य रूप से 32-बिट प्रक्रिया को 2GB पता स्थान तक सीमित करता है। लेकिन इसका मतलब यह नहीं है कि आपको 2GB आवंटित करने में सक्षम होने की उम्मीद करनी चाहिए!

पता स्थान पहले से ही आवंटित डेटा के सभी प्रकार से अटे पड़े हैं। स्टैक, और सभी असेंबली जो भरी हुई हैं, स्थिर चर और इतने पर हैं। इस बात की कोई गारंटी नहीं है कि 800MB सन्निहित असमान स्मृति कहीं भी होगी।

2 400MB चोक आवंटित करना शायद बेहतर किराया होगा। या 4 200 एमबी चंक्स। खंडित स्मृति स्थान के लिए छोटे आबंटन कमरे को खोजने के लिए बहुत आसान हैं।

वैसे भी, यदि आप इसे 12GB मशीन पर लागू करने जा रहे हैं, तो आप इसे 64-बिट अनुप्रयोग के रूप में चलाना चाहते हैं, जिसे सभी समस्याओं का समाधान करना चाहिए।


नौकरी को छोटे-छोटे हिस्सों में विभाजित करने से मेरे अपडेट को ऊपर देखने में मदद नहीं मिलती है।
m3ntat

4

32 से 64 बिट में परिवर्तन करना मेरे लिए काम कर रहा है - यदि आप 64 बिट पीसी पर हैं तो एक कोशिश के लायक है और इसे पोर्ट करने की आवश्यकता नहीं है।



1

32 बिट विंडो में 2GB प्रोसेस मेमोरी लिमिट है। / 3GB बूट विकल्प दूसरों ने उल्लेख किया है कि यह 3GB OS कर्नेल उपयोग के लिए सिर्फ 1gb शेष है। वास्तविक रूप से यदि आप बिना परेशानी के 2GB से अधिक का उपयोग करना चाहते हैं तो 64 बिट ओएस की आवश्यकता है। इससे यह समस्या भी समाप्त हो जाती है कि हालांकि आपके पास 4GB की भौतिक रैम हो सकती है, वीडियो कार्ड के लिए पुन: पता लगाया गया स्थान उस मेमोरी के एक बड़े आकार के चक को अनुपयोगी बना सकता है - आमतौर पर लगभग 500MB।


1

एक विशाल सरणी आवंटित करने के बजाय, क्या आप एक पुनरावृत्त का उपयोग करने की कोशिश कर सकते हैं? ये देरी से निष्पादित होने वाले हैं, जिसका अर्थ है कि मान केवल एक फ़ार्च स्टेटमेंट में मांगे जाने पर उत्पन्न होते हैं; आपको इस तरह से मेमोरी नहीं छोड़नी चाहिए:

private static IEnumerable<double> MakeRandomNumbers(int numberOfRandomNumbers) 
{
    for (int i = 0; i < numberOfRandomNumbers; i++)
    {
        yield return randomGenerator.GetAnotherRandomNumber();
    }
}


...

// Hooray, we won't run out of memory!
foreach(var number in MakeRandomNumbers(int.MaxValue))
{
    Console.WriteLine(number);
}

ऊपर आप जितने चाहें उतने रैंडम नंबर जेनरेट करेंगे, लेकिन उन्हें केवल उसी तरह से जेनरेट करें जैसे कि उनसे फॉरचेट स्टेटमेंट के जरिए मांगा जाता है। आप इस तरह से स्मृति से बाहर नहीं भागेंगे।

वैकल्पिक रूप से, यदि आपके पास उन सभी को एक स्थान पर होना चाहिए, तो उन्हें स्मृति में रखने के बजाय किसी फ़ाइल में संग्रहीत करें।


Iteresting दृष्टिकोण लेकिन मुझे अपने शेष एप्लिकेशन के किसी भी निष्क्रिय समय में एक यादृच्छिक संख्या रिपॉजिटरी के रूप में जितना संभव हो सके स्टोर करने की आवश्यकता है क्योंकि यह ऐप 24 घंटे की घड़ी पर कई भौगोलिक क्षेत्रों (कई मोंटे कार्लो सिमुलेशन रन) का समर्थन करता है, लगभग 70% दिन के अधिकतम सीपीयू लोड, पूरे दिन में शेष समय मैं सभी मुफ्त मेमोरी स्पेस में यादृच्छिक संख्याओं को बफर करना चाहता हूं। डिस्क पर संग्रहीत करना बहुत धीमा है और किसी भी लाभ को पराजित करना इस यादृच्छिक संख्या मेमोरी कैश में बफरिंग कर सकता है।
m3ntat

0

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


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


0

अपने समाधान को x64 में बदलें। यदि आप अभी भी एक समस्या का सामना कर रहे हैं, तो नीचे दिए गए अपवाद की तरह फेंकने वाली हर चीज़ को अधिकतम लंबाई प्रदान करें:

 var jsSerializer = new JavaScriptSerializer();
 jsSerializer.MaxJsonLength = Int32.MaxValue;

0

यदि आपको Visual Studio Hosting Process की आवश्यकता नहीं है:

विकल्प को अनचेक करें: प्रोजेक्ट-> गुण-> डीबग-> विज़ुअल स्टूडियो होस्टिंग प्रक्रिया को सक्षम करें

और फिर निर्माण।

यदि आप अभी भी समस्या का सामना कर रहे हैं:

प्रोजेक्ट-> प्रॉपर्टीज-> बिल्ड इवेंट्स-> इवेंट बिल्ड कमांड लाइन को पोस्ट करें और निम्नलिखित पेस्ट करें:

call "$(DevEnvDir)..\..\vc\vcvarsall.bat" x86
"$(DevEnvDir)..\..\vc\bin\EditBin.exe" "$(TargetPath)"  /LARGEADDRESSAWARE

अब, परियोजना का निर्माण।


-2

Windows प्रक्रिया की सीमा बढ़ाकर 3Gb कर दें। (boot.ini या Vista बूट मैनेजर के माध्यम से)


वास्तव में? डिफ़ॉल्ट अधिकतम प्रक्रिया मेमोरी क्या है? और इसे कैसे बदलें? अगर मैं अपने पीसी पर कोई गेम या कुछ खेलता हूं तो वह आसानी से एक एकल EXE / प्रोसेस से 2+ जीबी का उपयोग कर सकता है, मुझे नहीं लगता कि यह यहां समस्या है।
m3ntat

/ 3GB इसके लिए ओवरकिल है, और बहुत अधिक अस्थिरता पैदा कर सकता है क्योंकि कई ड्राइवर यह मान लेते हैं कि उपयोगकर्ता पॉइंटर्स हमेशा कम 2GB की ओर इशारा करते हैं।
जालफ

1
m3ntat: नहीं, 32-बिट विंडोज में, एक एकल प्रक्रिया 2GB के लिए विवश है। पता स्थान के शेष 2GB का उपयोग कर्नेल द्वारा किया जाता है।
जालफ

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.