2015-10-27 2 views
4

У меня есть 13 файлов .xlsx с примерно 1000 строк в каждом из них. Теперь я хочу объединить его в один файл .xlsx с одним листом. Я использую код отсюда http://blog.sodhanalibrary.com/2014/11/merge-excel-files-using-java.html#.Vi9ns36rSUk.Верхний предел GC превышен с Apache POI

Вот мой код (несколько изменений, метод addSheet без изменений)

try { 
     FileInputStream excellFile1 = new FileInputStream(new File("tmp_testOut1000.xlsx")); 
     XSSFWorkbook workbook1 = new XSSFWorkbook(excellFile1); 
     XSSFSheet sheet1 = workbook1.getSheetAt(0); 

     for(int i = 2; i < 14; i++){ 
      FileInputStream excellFile2 = new FileInputStream(new File("tmp_testOut" + i + "000.xlsx")); 
      XSSFWorkbook workbook2 = new XSSFWorkbook(excellFile2); 
      XSSFSheet sheet2 = workbook2.getSheetAt(0); 
      System.out.println("add " + i); 
      addSheet(sheet1, sheet2); 
     } 

     excellFile1.close(); 

     // save merged file 
     System.out.println("merging"); 
     File mergedFile = new File("merged.xlsx"); 
     if (!mergedFile.exists()) { 
      mergedFile.createNewFile(); 
     } 
     FileOutputStream out = new FileOutputStream(mergedFile); 
     System.out.println("write"); 
     workbook1.write(out); 
     out.close(); 
     System.out.println("Files were merged succussfully"); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 

Все файлы загружаются и сливающиеся, но после «писать» SYSOUT Я получаю

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded 
at org.apache.xmlbeans.impl.store.Xobj.new_cursor(Xobj.java:1829) 
at org.apache.xmlbeans.impl.values.XmlObjectBase.newCursor(XmlObjectBase.java:293) 
at org.apache.xmlbeans.impl.values.XmlComplexContentImpl.arraySetterHelper(XmlComplexContentImpl.java:1151) 
at org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTFontsImpl.setFontArray(Unknown Source) 
at org.apache.poi.xssf.model.StylesTable.writeTo(StylesTable.java:424) 
at org.apache.poi.xssf.model.StylesTable.commit(StylesTable.java:496) 
at org.apache.poi.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:341) 
at org.apache.poi.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:345) 
at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:206) 
at Start.main(Start.java:275) 

Что я могу сделать? Почему это происходит и как его предотвратить?

ответ

1

Эта проблема возникает из-за следующую причину

java.lang.OutOfMemoryError: GC предел накладных расходов превысил ошибки это способ JVM о сигнализации, что ваше приложение тратит слишком много времени, делая сбор мусора слишком мало результата. По умолчанию JVM настроен на то, чтобы выбросить эту ошибку, если он тратит более 98% общего времени на выполнение GC, и после того, как после GC восстанавливается только менее 2% кучи.

если вы просто хотите, чтобы игнорировать этот вопрос вы можете задать следующие параметры ВМ:

-XX:-UseGCOverheadLimit 

См link on GC overhead для получения дополнительной информации.

Вы также можете использовать следующие переключатели, чтобы назначить больше памяти кучи вашему приложению. Запустите пилот в своем приложении в течение некоторого времени и определите, сколько памяти будет лучше для вашего приложения.

-Xms128m -Xmx512m(these switches sets the initial heap memory size to 128mb and Max memory to 512mb) 
+0

Возможно ли без изменения настроек vm? Если нет, можете ли вы порекомендовать какую-нибудь другую библиотеку, которая может справиться с этим без превышения ограничений GC? – Marquess

+0

Я думаю, что библиотека не проблема, так как у вас хранятся огромные объемы данных в памяти, я бы предложил либо изучить исходный код, с помощью которого вы можете оптимизировать свое решение и/или изменить распределение размеров кучи для своего приложения, используя -Xms -Xmx. –

2

Попробуйте выделить больше памяти, например.

java -Xmx8192m 

Кроме того, что вы можете попробовать это объединить в один XLSX файл в то время, вместо того, чтобы загружать их все сразу.

Вы также можете переместить эту линию в цикл:

excellFile1.close(); 

Так вы закроете его сразу же.

10

POI, как известно, голоден в памяти, поэтому исчерпание памяти не является редкостью при обработке больших файлов Excel.

Когда вы сможете загрузить все исходные файлы и получить только проблемы при записи в единый файл, вы можете попробовать с помощью SXSSFWorkbook вместо XSSFWorkbook и делать регулярных промывок после добавления определенного количества контента (см Пои- документации org.apache.poi.xssf.streaming -package). Таким образом, вам не придется хранить весь сгенерированный файл в памяти, а только небольшие порции.

0

Если вы можете избежать использования удобных, но голодных API рабочей тетради, работайте вместо этого с потоковой логикой обработки данных по строкам, что значительно повышает эффективность памяти.

В частности, обратите особое внимание на использование: XSSFReader.SheetIterator для обхода листов.

И, наконец, взгляните на использование API: XSSFSheetXMLHandler. Для обработки строк с листом.

Смотрите код на этом проекте: https://github.com/jeevatkm/excelReader/blob/master/src/main/java/com/myjeeva/poi/ExcelReader.java

Вы определяете, как вы хотите обработать каждую строку, создавая свои собственные: новый SheetContentsHandler ....

Это совсем как SAX синтаксического анализа, то не займет немного места у вашего барана.

private void readSheet(StylesTable styles, ReadOnlySharedStringsTable sharedStringsTable, 
     InputStream sheetInputStream) throws IOException, ParserConfigurationException, SAXException { 

    SAXParserFactory saxFactory = SAXParserFactory.newInstance(); 
    XMLReader sheetParser = saxFactory.newSAXParser().getXMLReader(); 

    ContentHandler handler = 
     new XSSFSheetXMLHandler(styles, sharedStringsTable, sheetContentsHandler, true); 

    sheetParser.setContentHandler(handler); 
    sheetParser.parse(new InputSource(sheetInputStream)); 
    } 
Смежные вопросы