2015-11-10 5 views
7

मैं क्रियान्वित किया है (जावा में) एक पुनरावर्ती निर्देशिका संरचना में फ़ाइलों के नाम वापस जाने के लिए एक काफी सरल इटरेटर, और 2300 फ़ाइलों के बाद यह असफल लिस्टिंग जबकि "सिस्टम में बहुत सारी खुली फाइलें" (विफलता वास्तव में कक्षा को लोड करने की कोशिश में थी, लेकिन मुझे लगता है कि निर्देशिका सूची अपराधी थी)।विफलता "प्रणाली में बहुत अधिक फ़ाइलें खुली" एक पुनरावर्ती निर्देशिका संरचना

डेटा संरचना इटरेटर द्वारा बनाए रखा एक ढेर निर्देशिका जो प्रत्येक स्तर पर खुले हैं की सामग्री को पकड़े है।

वास्तविक तर्क काफी बुनियादी है:

private static class DirectoryIterator implements Iterator<String> { 

     private Stack<File[]> directories; 
     private FilenameFilter filter; 
     private Stack<Integer> positions = new Stack<Integer>(); 
     private boolean recurse; 
     private String next = null; 

     public DirectoryIterator(Stack<File[]> directories, boolean recurse, FilenameFilter filter) { 
      this.directories = directories; 
      this.recurse = recurse; 
      this.filter = filter; 
      positions.push(0); 
      advance(); 
     } 

     public boolean hasNext() { 
      return next != null; 
     } 

     public String next() { 
      String s = next; 
      advance(); 
      return s; 
     } 

     public void remove() { 
      throw new UnsupportedOperationException(); 
     } 

     private void advance() { 
      if (directories.isEmpty()) { 
       next = null; 
      } else { 
       File[] files = directories.peek(); 
       while (positions.peek() >= files.length) { 
        directories.pop(); 
        positions.pop(); 
        if (directories.isEmpty()) { 
         next = null; 
         return; 
        } 
        files = directories.peek(); 
       } 
       File nextFile = files[positions.peek()]; 
       if (nextFile.isDirectory()) { 
        int p = positions.pop() + 1; 
        positions.push(p); 
        if (recurse) { 
         directories.push(nextFile.listFiles(filter)); 
         positions.push(0); 
         advance(); 
        } else { 
         advance(); 
        } 
       } else { 
        next = nextFile.toURI().toString(); 
        count++; 
        if (count % 100 == 0) { 
         System.err.println(count + " " + next); 
        } 
        int p = positions.pop() + 1; 
        positions.push(p); 
       } 
      } 
     } 
    } 

मैं समझने के लिए इस कितने "खुली फ़ाइलों की आवश्यकता है" चाहते हैं। इस परिस्थिति में यह एल्गोरिदम एक फ़ाइल "खोलने" है, और यह फिर से बंद हो जाता है?

मैं कुछ साफ कोड जावा 7 या जावा 8 का उपयोग कर देखा है, लेकिन मैं जावा 6.

+0

बस 1,000,000 से अधिक फाइलों के साथ एक फाइल सिस्टम पर अपने कोड भाग गया, और समस्या आप देख रहे हैं नहीं मिलता है। मैं विंडोज़ पर जेडीके 1.6.0_34 का उपयोग कर रहा हूं। शायद समस्या कोड में कहीं और है? क्या आप 'FilenameFilter' के लिए कोड पोस्ट कर रहे हैं जिसका उपयोग आप कर रहे हैं? यह एक समस्या हो सकती है। – msandiford

+0

यह हो सकता है कि आपकी फाइल सिस्टम गहरी नहीं है, इसलिए जीसी द्वारा संसाधनों को ओएस में वापस कर दिया जा रहा है। या हो सकता है कि आपके ओएस की खुली फाइलों पर बड़ी सीमा हो। –

+0

हां, मैं कल रात जाग रहा था कि फ़ाइल नामफिल्टर को दोषी ठहराया गया था या नहीं। लेकिन नहीं: स्वीकृति() विधि 'नई फ़ाइल (डीआईआर, नाम) .isDirectory() || लौटाती है pattern.matcher (name) .matches(); ' –

उत्तर

6

जब आप nextFile.listFiles() कहते हैं, एक अंतर्निहित फ़ाइल वर्णनकर्ता निर्देशिका को पढ़ने के लिए खोला जाता है करने के लिए विवश कर रहा हूँ । इस वर्णनकर्ता को स्पष्ट रूप से बंद करने का कोई तरीका नहीं है, इसलिए आप कचरा संग्रह पर भरोसा कर रहे हैं। चूंकि आपका कोड गहरे पेड़ से निकलता है, यह अनिवार्य रूप से अगलीफाइल उदाहरणों का ढेर इकट्ठा कर रहा है जिसे एकत्रित नहीं किया जा सकता है।

चरण 1: अग्रिम कॉल करने से पहले सेट nextFile = बातिल()। यह कचरा संग्रह के लिए वस्तु जारी करता है।

चरण 2: आप nextFile nulling त्वरित कचरा संग्रहण के लिए प्रोत्साहित करने के बाद) System.gc (कॉल करने के लिए आवश्यकता हो सकती है। दुर्भाग्यवश, जीसी को मजबूर करने का कोई तरीका नहीं है।

चरण 3: आप अपने ऑपरेटिंग सिस्टम पर खुली हुई फ़ाइल सीमा बढ़ाने के लिए आवश्यकता हो सकती है। लिनक्स पर यह ulimit (1) के साथ किया जा सकता है।

आप जावा 7 या बाद में माइग्रेट कर सकते हैं, तो DirectoryStream आपकी समस्या का समाधान होगा। DirectFile.listFiles() का उपयोग करने के बजाय, DirectoryStream प्राप्त करने के लिए Files.newDirectoryStream (nextFile.toPath()) का उपयोग करें। फिर आप स्ट्रीम पर फिर से चालू हो सकते हैं और फिर ऑपरेटिंग सिस्टम संसाधनों को रिलीज़ करने के लिए इसे बंद कर सकते हैं। प्रत्येक लौटाए गए पथ को फ़ाइल में वापस फ़ाइल में परिवर्तित किया जा सकता है()। हालांकि आप फ़ाइल के बजाए बस पथ का उपयोग करने के लिए रिफैक्टर करना चाहेंगे।

+0

ओप का उल्लेख है कि वह जावा 6 के लिए बाध्य है। –

+0

आप सही हैं, पथ केवल> = जावा 7 है। मैं जावा 6 विकल्प के साथ अपना उत्तर संपादित करूंगा। –

1

मदद और सलाह के लिए सभी को धन्यवाद। मैंने स्थापित किया कि वास्तव में समस्या यह है कि फ़ाइलों के साथ पुनरावर्तक द्वारा लौटाए जाने के बाद क्या किया जा रहा है: "क्लाइंट" कोड फाइलों को खोलने के बाद खोल रहा है, और ठीक से जांच नहीं कर रहा है। यह तथ्य इस बात से जटिल है कि वापस आने वाली फाइलें वास्तव में समानांतर में संसाधित की जा रही हैं।

मैं भी DireectoryIterator है, जो मैं किसी बैठाना का हिस्सा फिर से लिख दिया है रुचि:

private static class DirectoryIterator implements Iterator<String> { 

     private Stack<Iterator<File>> directories; 
     private FilenameFilter filter; 
     private boolean recurse; 
     private String next = null; 

     public DirectoryIterator(Stack<Iterator<File>> directories, boolean recurse, FilenameFilter filter) { 
      this.directories = directories; 
      this.recurse = recurse; 
      this.filter = filter; 
      advance(); 
     } 

     public boolean hasNext() { 
      return next != null; 
     } 

     public String next() { 
      String s = next; 
      advance(); 
      return s; 
     } 

     public void remove() { 
      throw new UnsupportedOperationException(); 
     } 

     private void advance() { 
      if (directories.isEmpty()) { 
       next = null; 
      } else { 
       Iterator<File> files = directories.peek(); 
       while (!files.hasNext()) { 
        directories.pop(); 
        if (directories.isEmpty()) { 
         next = null; 
         return; 
        } 
        files = directories.peek(); 
       } 
       File nextFile = files.next(); 
       if (nextFile.isDirectory()) { 
        if (recurse) { 
         directories.push(Arrays.asList(nextFile.listFiles(filter)).iterator()); 
        } 
        advance(); 
       } else { 
        next = nextFile.toURI().toString(); 
       } 
      } 
     } 
    } 
संबंधित मुद्दे