2012-08-30 5 views
15

मेरे पास टेक्स्ट की 20 मिलियन लाइनों के साथ एक बड़ी टेक्स्ट फ़ाइल है। जब मैं निम्न प्रोग्राम का उपयोग कर फ़ाइल पढ़ता हूं, तो यह ठीक काम करता है, और वास्तव में मैं बहुत बड़ी फ़ाइलों को पढ़ सकता हूं जिनमें कोई स्मृति समस्या नहीं है।मेरा जावा प्रोग्राम जो एक बड़ी टेक्स्ट फ़ाइल पढ़ता है वह स्मृति से बाहर हो रहा है, क्या कोई यह समझाने में मदद कर सकता है कि क्यों?

public static void main(String[] args) throws IOException { 
    File tempFile = new File("temp.dat"); 
    String tempLine = null; 
    BufferedReader br = null; 
    int lineCount = 0; 
    try { 
     br = new BufferedReader(new FileReader(tempFile)); 
     while ((tempLine = br.readLine()) != null) { 
      lineCount += 1; 
     } 
    } catch (Exception e) { 
     System.out.println("br error: " +e.getMessage()); 
    } finally { 
     br.close(); 
     System.out.println(lineCount + " lines read from file"); 
    } 
} 

लेकिन अगर मैं इसे पढ़ने से पहले इस फाइल करने के लिए कुछ रिकॉर्ड संलग्न करने के लिए की जरूरत है, BufferedReader स्मृति की एक बड़ी राशि (मैं सिर्फ इस पर नजर रखने के लिए Windows कार्य प्रबंधक का इस्तेमाल किया है, न बहुत वैज्ञानिक मैं जानता हूँ कि खपत लेकिन यह समस्या का प्रदर्शन करता है)। संशोधित कार्यक्रम नीचे है, जो पहले जैसा ही है, सिवाय इसके कि मैं पहले फ़ाइल में एक रिकॉर्ड जोड़ रहा हूं।

public static void main(String[] args) throws IOException { 
    File tempFile = new File("temp.dat"); 
    PrintWriter pw = null; 
    try { 
     pw = new PrintWriter(new BufferedWriter(new FileWriter(tempFile, true))); 
     pw.println(" "); 
    } catch (Exception e) { 
     System.out.println("pw error: " + e.getMessage()); 
    } finally { 
     pw.close(); 
    } 

    String tempLine = null; 
    BufferedReader br = null; 
    int lineCount = 0; 
    try { 
     br = new BufferedReader(new FileReader(tempFile)); 
     while ((tempLine = br.readLine()) != null) { 
      lineCount += 1; 
     } 
    } catch (Exception e) { 
     System.out.println("br error: " +e.getMessage()); 
    } finally { 
     br.close(); 
     System.out.println(lineCount + " lines read from file"); 
    } 
} 

विंडोज कार्य प्रबंधक, जब मैं कार्यक्रम के दूसरे संस्करण को चलाने जहां लाइन में बड़ी टक्कर स्मृति की खपत से पता चलता का स्क्रीनशॉट।

task manager screenshot

तो मैं स्मृति से बाहर चलने के बिना इस फाइल को पढ़ने में सक्षम था। लेकिन मेरे पास 50 मिलियन से अधिक रिकॉर्ड वाले बहुत बड़ी फाइलें हैं, जो कि जब मैं इस कार्यक्रम को उनके खिलाफ चलाता हूं तो स्मृति अपवाद से बाहर निकलता है? क्या कोई समझा सकता है कि प्रोग्राम का पहला संस्करण किसी भी आकार की फाइलों पर ठीक क्यों काम करता है, लेकिन दूसरा प्रोग्राम विफलता में समाप्त होता है और विफलता में समाप्त होता है? मैं विंडोज 7 पर के साथ चल रहा हूँ:

जावा संस्करण "1.7.0_05"
जावा (टीएम) एसई रनटाइम वातावरण (निर्माण 1.7.0_05-B05)
जावा हॉटस्पॉट (टीएम) क्लाइंट वी एम (निर्माण 23.1-B03 , मिश्रित मोड, साझाकरण)

+1

यह 'BufferedReader' कि सभी स्मृति लेता है के लिए कुछ अच्छा विश्लेषण के साथ VMs चलने से ढेर-डंप पाने के लिए? मुझे संदेह होगा कि यह 'फाइलवाइटर' होगा। –

+1

क्या मिश्रण में 'BufferedWriter' जोड़ने का कोई कारण है? यदि आप 'नया प्रिंटवाइटर (नया फ़ाइलवाइटर (...)) करते हैं तो क्या आपको अभी भी वही समस्या मिलती है? –

+2

(प्रश्न के साथ कुछ भी करने के लिए नहीं, लेकिन मुझे यह इंगित करना होगा कि आप अंत में ब्लॉक में एनपीई प्राप्त कर सकते हैं। इसका निपटारा करने का तरीका जावा एसई 7 के साथ-साथ संसाधन का उपयोग करना है, या जावा एसई 6 उपयोग के साथ अंत में और पकड़ने के लिए अलग प्रयास करें और नल के उपयोग से बचें।) –

उत्तर

-3

आपको जावा को एक बड़े ढेर के साथ शुरू करने की आवश्यकता होगी। जावा कमांड पर पैरामीटर के रूप में -Xmx1024m आज़माएं।

असल में आपको फ़ाइल के आकार की तुलना में अधिक स्मृति की आवश्यकता होगी।

tempLine = br.readLine() 

मैं हर बार जब आप ReadLine (फोन विश्वास करते हैं) यह शायद एक नया स्ट्रिंग वस्तु जिस पर छोड़ दिया जाता है पैदा कर रही है:

+6

क्या आप समझा सकते हैं कि मुझे क्यों चाहिए दूसरे कार्यक्रम के लिए एक बड़ा ढेर लेकिन 1 नहीं? कार्यक्रम का पहला संस्करण ठीक काम करता है, और एक बहुत छोटा ढेर आकार का उपयोग करता है। BufferedReader एक समय में फ़ाइल 1 लाइन को संसाधित करता है, इसलिए इसे बहुत मेमोरी की आवश्यकता नहीं है? –

+0

मैं tony_h से सहमत हूं। –

0

हर बार जब आप जावा दिनचर्या निम्नलिखित जावा निष्पादित, तो आप एक नया वस्तु पैदा कर रहे प्रत्येक बार फिर से असाइनमेंट को tempLine के मान को असाइन करने के लिए बुलाया जाता है।

इसलिए, चूंकि जीसी को लगातार हजारों वस्तुओं को नहीं कहा जा रहा है, तो सेकंड के भीतर हीप पर छोड़ा जा सकता है।

कुछ लोग कहते हैं कि System.gc() प्रत्येक 1000 लाइनों या तो कॉल करने का बुरा विचार है, लेकिन अगर यह आपकी समस्या को हल करता है तो मैं उत्सुक होगा।

tempLine=null; 
+0

मुझे नहीं लगता कि यह समस्या है। जब मैं प्रोग्राम के रीडोनली संस्करण को चलाता हूं, तो BufferedReader बिल्कुल मेमोरी समस्याओं के साथ ठीक काम करता है। समस्या तब होती है जब मैं फ़ाइल के पढ़ने से पहले एक सेक्शन के साथ फ़ाइल लिखता हूं जो एक प्रिंटराइटर का उपयोग कर फ़ाइल में एक लाइन जोड़ता है। –

+0

अपवाद पर आपकी लाइन गिनती क्या है? इसके अलावा, यदि आप जेडीके 1.6.0_22 या उच्चतर का उपयोग करते हैं, तो मेरा मानना ​​है कि आपको एक बहुप्रचारित कचरा कलेक्टर मिलता है और मैं उत्सुक हूं कि आप इसके साथ क्या व्यवहार करते हैं? इसके अलावा, BufferedWriter आपको बफर आकार बढ़ाने की अनुमति नहीं देता है? वैकल्पिक: इनपुट को पढ़ने और फिर डेटा को स्टोर करने के लिए इनपुटस्ट्रीम रीडर और फ़ाइल इनपुटस्ट्रीम का उपयोग करने का प्रयास करें, फिर फ़ाइल आउटपुटस्ट्रीम का उपयोग करके उस char को लिखें। – djangofan

0
 pw = new PrintWriter(new BufferedWriter(new FileWriter(tempFile, true))); 

आप एक BufferedWriter का उपयोग नहीं की कोशिश की थी: इसके अलावा, आप मूल रूप से कचरा संग्रहणीय रूप में प्रत्येक वस्तु को चिह्नित करने के लिए प्रत्येक पंक्ति के बाद यह आदेश चला सकता है? यदि आप अंत में कुछ लाइनों को जोड़ रहे हैं तो आपको बफर की आवश्यकता नहीं है? यदि आप करते हैं, तो बाइट सरणी (संग्रह या स्ट्रिंग बिल्डर) का उपयोग करने पर विचार करें। आखिरकार आपने जावा 1.6_32 में ऐसा करने की कोशिश की? लेखकों में से एक के नए संस्करण में एक बग हो सकता है।

क्या आप pw.close() के पहले और बाद में निःशुल्क मेमोरी प्रिंट कर सकते हैं; ?

System.out.println("before wr close :" + Runtime.getRuntime().freeMemory()); 

और बाद इसी तरह करीब है और करीब

0

क्योंकि आप सब पर लाइनफीड नहीं किया जा सकता हो रही है आपकी फ़ाइल में/गाड़ी वापसी यह हो सकता है पाठक के बाद। इस मामले में, readLine() आपकी फ़ाइल से केवल एक ही स्ट्रिंग बनाने की कोशिश करता है जो शायद स्मृति से बाहर हो रहा है। ReadLine() की

जावा दस्तावेज़:

Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.

+0

दुर्भाग्यवश यह समस्या नहीं है, फ़ाइलों को ठीक से चित्रित किया गया है, और मुझे फ़ाइलों को पार्स करने के रूप में सही पंक्ति गणना मिल रही है। –

0

आप की कोशिश की है:

ए) एक नई फ़ाइल उदाहरण बनाकर पढ़ने के लिए उपयोग करने के लिए है, लेकिन एक ही फाइल की ओर इशारा करते। और बी) दूसरे भाग में एक पूरी तरह से अलग फ़ाइल पढ़ना।

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

यह कोड के साथ कोई समस्या नहीं दिखता है, और सोचने के लिए आपका तर्क यह नहीं लगता कि इसे तोड़ना नहीं चाहिए, इसलिए यह कुछ अंतर्निहित कार्यक्षमता होनी चाहिए।

+0

धन्यवाद @ ग्लेन लैम्ब, मुझे लगता है कि आपके सुझाव बहुत समझ में आते हैं। हालांकि मैंने इस मुद्दे पर पहले से ही काफी समय बिताया था और आखिरकार इसे एक और तरीका करने का फैसला किया जिसने इस समस्या से पूरी तरह से बचाया।अगर मुझे कभी वापस लौटने का समय मिलता है, तो मुझे कोई भी परिणाम मिल जाएगा। –

1

आप VM-Options

-XX:+HeapDumpOnOutOfMemoryError 

के साथ एक जावा वी एम शुरू कर सकते हैं कि यह एक फ़ाइल है, जो खोजने रिसाव संदिग्धों

उपयोग के लिए विश्लेषण किया जा सकता करने के लिए एक ढेर डंप लिखेंगे एक '+' एक जोड़ने के लिए विकल्प और एक विकल्प को हटाने के लिए '-'।

आप ग्रहण जावा मेमोरी विश्लेषक प्लगइन MAT उपयोग कर रहे हैं लीक संदिग्ध आदि

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

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