2012-05-03 13 views
6

मेरे पास कई बड़ी gzip फ़ाइलें (लगभग 10 एमबी - 200 एमबी) हैं जिन्हें मैंने ftp से डाउनलोड करने के लिए डाउनलोड किया है।जीजेआईपी डीकंप्रेशन सी # आउटऑफमेमरी

तो मैंने Google पर कोशिश की और gzip decompression के लिए कुछ समाधान ढूंढने की कोशिश की।

static byte[] Decompress(byte[] gzip) 
    { 
     using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress)) 
     { 
      const int size = 4096; 
      byte[] buffer = new byte[size]; 
      using (MemoryStream memory = new MemoryStream()) 
      { 
       int count = 0; 
       do 
       { 
        count = stream.Read(buffer, 0, size); 
        if (count > 0) 
        { 
         memory.Write(buffer, 0, count); 
        } 
       } 
       while (count > 0); 
       return memory.ToArray(); 
      } 
     } 
    } 

यह 50MB नीचे किसी भी फाइल के लिए अच्छी तरह से काम, लेकिन मैं स्मृति अपवाद से बाहर प्रणाली मिल गया है एक बार मैं 50MB से इनपुट अधिक है। अपवाद से पहले अंतिम स्थिति और स्मृति की लंबाई 134217728 है। मुझे नहीं लगता कि यह मेरी भौतिक स्मृति से संबंधित है, मैं समझता हूं कि 32-बिट का उपयोग करने के बाद से मुझे 2 जीबी से अधिक ऑब्जेक्ट नहीं हो सकता है।

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

मेरे सवालों का

  • कारण है कि मैं System.OutMemoryException मिला?
  • gzip फ़ाइलों को डिकंप्रेस करने के लिए सबसे अच्छा संभव समाधान क्या है और बाद में कुछ टेक्स्ट प्रोसेसिंग करें?
+0

आप स्ट्रीम की पूरी सामग्री को स्मृति में लोड कर रहे हैं और इसे बाइट सरणी के रूप में वापस कर रहे हैं। स्मृति अपवाद से बाहर * अन्य * की अपेक्षा आप और क्या करेंगे? आपको इसे सब कुछ स्मृति में लोड नहीं करना चाहिए - अंत में आप सरणी के साथ क्या करना चाहते हैं? इसे एक फाइल में लिखें? जो भी आप चाहते हैं, यह स्ट्रीम-आधारित होना चाहिए, और सरणी-आधारित नहीं होना चाहिए। –

+0

अच्छी तरह से .. अपवाद स्मृति.write पर होता है और 134217728 में वहां अटक गया .. मैं स्मृति प्रबंधन से परिचित नहीं हूं, इसलिए कृपया मेरे साथ भालू। बाद में मैं सभी संसाधित फ़ाइलों को डेटाबेस में सहेज दूंगा, gzipped फ़ाइलों के अंदर फ़ाइल सीएसवी फ़ाइल –

+3

निश्चित है, लेकिन अगर आप इसे संसाधित करते हैं तो आपका डिज़ाइन बेहतर होगा * जबकि * आप इसे अनजिप कर रहे हैं। इस तरह आपको इसे संभालने के लिए स्मृति का एक बड़ा हिस्सा आवंटित नहीं करना पड़ेगा। (उदाहरण के लिए, अपनी जीजीआईपी स्ट्रीम को सीधे 'स्ट्रीम रीडर' में फेंककर) –

उत्तर

3

मेमोरी पर मेमोरीस्ट्रीम के लिए आवंटन रणनीति बड़ी मात्रा में डेटा के लिए अनुकूल नहीं है।

चूंकि मेमोरीस्ट्रीम के लिए अनुबंध अंतर्निहित स्टोरेज के रूप में संगत सरणी रखना है, इसे अक्सर बड़ी धारा के लिए सरणी को पुन: आवंटित करना होता है (अक्सर लॉग 2 (size_of_stream) के रूप में)। इस तरह के पुनः आबंटन के दुष्प्रभाव

  • पुनः आबंटन पर लंबे समय प्रतिलिपि देरी कर रहे हैं
  • नई सरणी मुक्त पता स्थान पहले से ही भारी पिछले आवंटन से खंडित में फिट होगा
  • नई सरणी LOH ढेर अपनी quirks है पर होगा (कोई जीसी 2 पर संकलन, संग्रह)।

मेमोरीस्ट्रीम के माध्यम से बड़े (100 एमबी +) स्ट्रीम को संभालने के परिणामस्वरूप x86 सिस्टम पर स्मृति अपवाद से बाहर होने की संभावना है। इसके अलावा डेटा लौटने के लिए सबसे आम पैटर्न है GetArray को कॉल करने के लिए, जो अतिरिक्त रूप से उसी स्पेस के बारे में आवश्यक है जैसे मेमोरीस्ट्रीम के लिए उपयोग किया गया अंतिम सरणी बफर।

प्रयास हल करने के लिए:

  • सबसे सस्ता तरीका पूर्व विकसित करने के लिए MemoryStream आकार आप की जरूरत का अनुमान लगाने का है (अधिमानतः थोड़ा बड़े)। आप फर्जी स्ट्रीम को पढ़कर आवश्यक आकार का पूर्व-गणना कर सकते हैं जो कुछ भी स्टोर नहीं करता है (CPU संसाधनों का अपशिष्ट, लेकिन आप इसे पढ़ने में सक्षम होंगे)। बाइट सरणी के बजाय स्ट्रीम लौटने पर विचार करें (या लंबाई के साथ मेमोरीस्ट्रीम बफर के बाइट सरणी को वापस करें)।
  • यदि आपको पूरी स्ट्रीम या बाइट सरणी की आवश्यकता है तो इसे संभालने का एक और विकल्प है बड़ी मात्रा में डेटा स्टोर करने के लिए मेमोरीस्ट्रीम की बजाय अस्थायी फ़ाइल स्ट्रीम का उपयोग करना।
  • अधिक जटिल दृष्टिकोण धारा को कार्यान्वित करना है जो LOH पर आवंटन से बचने के लिए छोटे डेटा (यानी 64 के) ब्लॉक में अंतर्निहित डेटा का हिस्सा है और धारा को बढ़ने की आवश्यकता होने पर डेटा कॉपी करना है।
+0

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

0

मैं समझता हूँ कि मैं वस्तु अधिक 2GB से नहीं हो सकता है के बाद से मैं 32-बिट

यह गलत है का उपयोग करें। आपको जितनी जरूरत हो उतनी मेमोरी हो सकती है। 32-बिट सीमा का मतलब है कि वर्चुअल एड्रेस स्पेस के पास केवल 4 जीबी (ओएस इसे आधा ले सकता है) हो सकता है। वर्चुअल एड्रेस स्पेस मेमोरी नहीं है। Here एक अच्छा पढ़ा है।

मुझे System.OutMemoryException क्यों मिला?

क्योंकि आवंटक को आपके ऑब्जेक्ट के लिए सम्मिलित पता स्थान नहीं मिल सका या यह बहुत तेज होता है और यह क्लोग होता है। (सबसे अधिक संभावना पहले)

gzip फ़ाइलों को डीकंप्रेस करने के लिए सबसे अच्छा संभव समाधान क्या है और कुछ टेक्स्ट प्रोसेसिंग बाद में करें?

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

+5

[ओपी 2 जीबी * सरणी आकार * सीमा के बारे में सही है] (http: // stackoverflow।com/प्रश्न/1087982/एकल वस्तुओं-अभी भी सीमित करने के लिए 2-GB-इन-आकार-इन-clr-4-0)। साथ ही, मुझे लगता है कि बाहरी उपकरण का सुझाव देना जैसे कि 7-ज़िप पूरी तरह से इस खोजन की भावना को याद करता है। –

1

आप की तरह एक परीक्षण की कोशिश कर सकते एक OutOfMemoryException होने से पहले आप MemoryStream को लिख सकते हैं कितना का एहसास दिलाने के लिए निम्न:

 const int bufferSize = 4096; 
     byte[] buffer = new byte[bufferSize]; 

     int fileSize = 1000 * 1024 * 1024; 

     int total = 0; 

     try 
     { 
      using (MemoryStream memory = new MemoryStream()) 
      { 
       while (total < fileSize) 
       { 
        memory.Write(buffer, 0, bufferSize); 
        total += bufferSize; 
       } 

      } 

      MessageBox.Show("No errors"); 

     } 
     catch (OutOfMemoryException) 
     { 
      MessageBox.Show("OutOfMemory around size : " + (total/(1024m * 1024.0m)) + "MB"); 
     } 

आपको सबसे पहले एक अस्थायी शारीरिक फाइल करने के लिए अनज़िप करने के लिए है और फिर से हो सकता है इसे छोटे टुकड़ों में पढ़ें, और प्रक्रिया के रूप में आप जाओ।

साइड प्वाइंट: दिलचस्प है, एक Windows XP पीसी पर, ऊपर कोड देता है: जब कोड लक्ष्य .NET 2.0 "आकार 256MB के आसपास OutOfMemory", और "आकार 512MB के आसपास OutOfMemory" .net 4.

+1

मैंने पहले से ही ऊपर निर्दिष्ट किया है। यदि मैं सही हूं तो यह लगभग 128 एमबी पर लगभग 134217728 पर फंस गया। मुझे यकीन नहीं है कि यह बहुत जल्दी क्यों होता है लेकिन मुझे लगता है कि मेमोरी स्ट्रीम चुनें मेरी पहली गलती है .. आपके उत्तर के लिए धन्यवाद –

+0

पुष्टि कर सकता है कि मैंने सटीक सीमा को मारा है। – Kris

1

क्या आप कई धागे में फ़ाइलों को संसाधित करते हैं? यह आपकी पता स्थान की एक बड़ी राशि का उपभोग करेगा। OutOfMemory त्रुटियां आमतौर पर भौतिक स्मृति से संबंधित नहीं होती हैं, और इसलिए मेमोरीस्ट्रीम आपके अपेक्षा से पहले बहुत दूर चला सकता है। इस चर्चा को http://social.msdn.microsoft.com/Forums/en-AU/csharpgeneral/thread/1af59645-cdef-46a9-9eb1-616661babf90 देखें। यदि आप 64-बिट प्रक्रिया में स्विच करते हैं, तो संभवतः आप फ़ाइल आकारों के लिए ठीक से अधिक होंगे।

हालांकि आपकी वर्तमान स्थिति में, आप मेमोरी मैप की गई फ़ाइलों के साथ काम कर सकते हैं ताकि किसी भी पता आकार सीमाएं प्राप्त हो सकें। यदि आप .NET 4.0 का उपयोग कर रहे हैं, तो यह विंडोज फ़ंक्शन http://msdn.microsoft.com/en-us/library/dd267535.aspx के लिए देशी रैपर प्रदान करता है।

+0

हां, मैंने एसओ में पूछने से पहले उस लिंक को देखा। मैं सिर्फ यह जानना चाहता हूं कि मेरे पास कौन से अन्य विकल्प हैं। आपको जवाब के लिए धन्यवाद –

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