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


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

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

अपवाद: टंकित की छूट 'व्यवस्था। स्मृति के बाहर छूट' फेंका गया था।

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

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

अद्यतन करें

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

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;

76
2017-07-20 13:50


मूल


मैं आपके आवेदन को पुन: व्यवस्थित करने की अनुशंसा करता हूं ताकि आपको इतनी मेमोरी का उपयोग न करना पड़े। आप क्या कर रहे हैं कि आपको एक बार में स्मृति में सौ मिलियन संख्या की आवश्यकता है? - Eric Lippert
आपने अपनी पेजफाइल या उस तरह मूर्खतापूर्ण कुछ अक्षम नहीं किया है, है ना? - jalf
@EricLippert, पी बनाम एनपी समस्या पर काम करते समय मैं इसमें भाग रहा हूं (claymath.org/millenium-problems/p-vs-np-problem)। क्या आपके पास वर्किंग मेमोरी उपयोग को कम करने के लिए कोई सुझाव है? (उदाहरण के लिए सी ++ डेटा प्रकार इत्यादि का उपयोग कर हार्ड डिस्क पर डेटा के हिस्सों को क्रमबद्ध और भंडारित करना) - devinbost
@bosit यह एक प्रश्न और उत्तर साइट है। यदि आपके पास वास्तविक कोड के बारे में एक विशिष्ट तकनीकी प्रश्न है, तो इसे एक प्रश्न के रूप में पोस्ट करें। - Eric Lippert
आपकी टिप्पणी में पी बनाम एनपी समस्या के लिए @bostIT लिंक अब मान्य नहीं है। - RBT


जवाब:


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

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

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

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

116
2017-07-20 13:58





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

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

23
2017-07-20 13:59





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

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

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

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

18
2018-06-26 13:56





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

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 जीबी सीएलआर ऑब्जेक्ट आकार सीमा (उसी ब्लॉग पर शुगी कॉक्यूक की एक टिप्पणी में उल्लिखित), स्मृति विखंडन पर चमक, और पृष्ठ फ़ाइल आकार के प्रभाव का उल्लेख करने में विफल रहता है (और यह कैसे हो सकता है के उपयोग के साथ संबोधित किया जाना चाहिए CreateFileMapping समारोह)।

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

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

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


यह तब तक लंबा रास्ता था। उम्मीद है कि यह किसी की मदद करता है। मैंने इसे पोस्ट किया क्योंकि मैं भाग गया था System.OutOfMemoryException 24 जीबी रैम वाले सिस्टम पर एक एक्स 64 प्रोग्राम चला रहा है, भले ही मेरी सरणी केवल 2 जीबी सामान रखती हो।


7
2017-12-24 23:15





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

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

हालांकि, विंडोज सामान्यतः 32-बिट प्रक्रिया को 2 जीबी एड्रेस स्पेस तक सीमित करता है। लेकिन इसका मतलब यह नहीं है कि आपको 2 जीबी आवंटित करने में सक्षम होना चाहिए!

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

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

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


5
2017-07-20 14:05



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


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


3
2017-08-31 21:04





यदि आपको ऐसी बड़ी संरचनाओं की आवश्यकता है, तो शायद आप मेमोरी मैप की गई फ़ाइलों का उपयोग कर सकते हैं। यह आलेख उपयोगी साबित हो सकता है: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx

एल.पी., डेजन


2
2017-07-20 14:09