2014-10-11 23 views
15

Attualmente sto cercando di utilizzare un parser SAX ma circa 3/4 attraverso il file si blocca completamente, ho provato ad allocare più memoria ecc. Ma non ho ottenuto alcun miglioramento.Come analizzare file XML (50 GB) in Java

C'è un modo per velocizzare questo? Un metodo migliore?

Spogliato a ossa nude, così ora ho il seguente codice e quando si esegue in linea di comando non va ancora veloce come vorrei.

Esecuzione con "java -Xms-4096m -Xmx8192m-jar reader.jar" ottengo un limite GC in testa superato attorno articolo 700000

principale:

public class Read { 
    public static void main(String[] args) {  
     pages = XMLManager.getPages(); 
    } 
} 

XMLManager

public class XMLManager { 
    public static ArrayList<Page> getPages() { 

    ArrayList<Page> pages = null; 
    SAXParserFactory factory = SAXParserFactory.newInstance(); 

    try { 

     SAXParser parser = factory.newSAXParser(); 
     File file = new File("..\\enwiki-20140811-pages-articles.xml"); 
     PageHandler pageHandler = new PageHandler(); 

     parser.parse(file, pageHandler); 
     pages = pageHandler.getPages(); 

    } catch (ParserConfigurationException e) { 
     e.printStackTrace(); 
    } catch (SAXException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 


    return pages; 
    }  
} 

PageHandler

public class PageHandler extends DefaultHandler{ 

    private ArrayList<Page> pages = new ArrayList<>(); 
    private Page page; 
    private StringBuilder stringBuilder; 
    private boolean idSet = false; 

    public PageHandler(){ 
     super(); 
    } 

    @Override 
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 

     stringBuilder = new StringBuilder(); 

     if (qName.equals("page")){ 

      page = new Page(); 
      idSet = false; 

     } else if (qName.equals("redirect")){ 
      if (page != null){ 
       page.setRedirecting(true); 
      } 
     } 
    } 

    @Override 
    public void endElement(String uri, String localName, String qName) throws SAXException { 

     if (page != null && !page.isRedirecting()){ 

      if (qName.equals("title")){ 

       page.setTitle(stringBuilder.toString()); 

      } else if (qName.equals("id")){ 

       if (!idSet){ 

        page.setId(Integer.parseInt(stringBuilder.toString())); 
        idSet = true; 

       } 

      } else if (qName.equals("text")){ 

       String articleText = stringBuilder.toString(); 

       articleText = articleText.replaceAll("(?s)<ref(.+?)</ref>", " "); //remove references 
       articleText = articleText.replaceAll("(?s)\\{\\{(.+?)\\}\\}", " "); //remove links underneath headings 
       articleText = articleText.replaceAll("(?s)==See also==.+", " "); //remove everything after see also 
       articleText = articleText.replaceAll("\\|", " "); //Separate multiple links 
       articleText = articleText.replaceAll("\\n", " "); //remove new lines 
       articleText = articleText.replaceAll("[^a-zA-Z0-9- \\s]", " "); //remove all non alphanumeric except dashes and spaces 
       articleText = articleText.trim().replaceAll(" +", " "); //convert all multiple spaces to 1 space 

       Pattern pattern = Pattern.compile("([\\S]+\\s*){1,75}"); //get first 75 words of text 
       Matcher matcher = pattern.matcher(articleText); 
       matcher.find(); 

       try { 
        page.setSummaryText(matcher.group()); 
       } catch (IllegalStateException se){ 
        page.setSummaryText("None"); 
       } 
       page.setText(articleText); 

      } else if (qName.equals("page")){ 

       pages.add(page); 
       page = null; 

      } 
     } else { 
      page = null; 
     } 
    } 

    @Override 
    public void characters(char[] ch, int start, int length) throws SAXException { 
     stringBuilder.append(ch,start, length); 
    } 

    public ArrayList<Page> getPages() { 
     return pages; 
    } 
} 
+0

Sei sicuro che ciò che è "congelamento" (vuoi darci ulteriori dettagli su cosa significhi per la tua situazione?) È il parser SAX piuttosto che qualcosa nel tuo codice? Mantieni gli oggetti in memoria ovunque nella tua applicazione? – Tim

+0

In questo momento sto facendo alcuni test su di esso, ma ho la sensazione che potrebbe essere stata l'eclissi che si stava congelando (lo spaccassi a nudo e tutto si bloccò). Eseguendolo attraverso la linea di comando al momento, tieniti aggiornato. –

+0

Aggiunto un codice di base che restituisce solo l'articolo del lettore all'interno del file xml –

risposta

24

Probabilmente il codice di analisi funziona correttamente, ma il volume di dati che si sta caricando è probabilmente troppo grande per essere conservato in memoria in tale ArrayList.

È necessario disporre di una sorta di pipeline per trasferire i dati alla destinazione effettiva senza mai memorizzare nella memoria in una sola volta.

Quello che ho fatto a volte per questo tipo di situazione è simile al seguente.

Creare un'interfaccia per l'elaborazione di un singolo elemento:

public interface PageProcessor { 
    void process(Page page); 
} 

Fornitura un'implementazione di questo al PageHandler attraverso un costruttore:

public class Read { 
    public static void main(String[] args) { 

     XMLManager.load(new PageProcessor() { 
      @Override 
      public void process(Page page) { 
       // Obviously you want to do something other than just printing, 
       // but I don't know what that is... 
       System.out.println(page); 
      } 
     }) ; 
    } 

} 


public class XMLManager { 

    public static void load(PageProcessor processor) { 
     SAXParserFactory factory = SAXParserFactory.newInstance(); 

     try { 

      SAXParser parser = factory.newSAXParser(); 
      File file = new File("pages-articles.xml"); 
      PageHandler pageHandler = new PageHandler(processor); 

      parser.parse(file, pageHandler); 

     } catch (ParserConfigurationException e) { 
      e.printStackTrace(); 
     } catch (SAXException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

    } 
} 

inviare dati a questo processore invece di metterla in lista:

public class PageHandler extends DefaultHandler { 

    private final PageProcessor processor; 
    private Page page; 
    private StringBuilder stringBuilder; 
    private boolean idSet = false; 

    public PageHandler(PageProcessor processor) { 
     this.processor = processor; 
    } 

    @Override 
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 
     //Unchanged from your implementation 
    } 

    @Override 
    public void characters(char[] ch, int start, int length) throws SAXException { 
     //Unchanged from your implementation 
    } 

    @Override 
    public void endElement(String uri, String localName, String qName) throws SAXException { 
      // Elide code not needing change 

      } else if (qName.equals("page")){ 

       processor.process(page); 
       page = null; 

      } 
     } else { 
      page = null; 
     } 
    } 

} 

Naturalmente, è possibile fai in modo che la tua interfaccia gestisca blocchi di più record invece di uno solo e fai in modo che lo PageHandler raccolga le pagine localmente in un elenco più piccolo e invii periodicamente l'elenco per l'elaborazione e cancelli l'elenco.

Oppure (forse meglio) è possibile implementare l'interfaccia PageProcessor come definito qui e creare in logica lì che memorizza i dati e li invia per ulteriore gestione in blocchi.

+0

Questo è quello che ho finito per fare, 10.000 pagine alla volta funziona bene. –

+0

Ho appena iniziato a esaminare un'attività simile. È una classe di pagina personalizzata? – Wudang

0

Questo è effettivamente il problema: pages.add(page);. In realtà SAX è molto sensibile alla memoria e l'utilizzo della memoria non dipende dalla dimensione del file di input.

Abbiamo progettato un generatore di codice che genera codice basato su XSD (se disponibile, in alternativa è possibile generarlo da un documento sorgente). Questo prodotto è basato su SAX e processa file multi-GB (il più grande che abbiamo utilizzato è 22 GB). Fa in modo di seguire l'approccio delineato da Don Roby qui. L'unica cosa che devi fare è implementare l'interfaccia del processore.

Il tempo di esecuzione (java) motore utilizza un file di configurazione (file di proprietà Java) che permette di abbonarsi ai tipi di schema a cui sei interessato Se volete avere maggiori informazioni su questo, date un'occhiata qui:. http://www.xml2java.net/xml-to-java-data-binding-for-big-data/