超过了Apache POI的GC开销限制

我有13个.xlsx文件,每个文件大约有1000行。 现在我想用一张表将它合并到一个.xlsx文件中。 我使用这里的代码http://blog.sodhanalibrary.com/2014/11/merge-excel-files-using-java.html#.Vi9ns36rSUk 。

这是我的代码(几个更改,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) 

我能做什么? 为什么会发生这种情况,以及如何预防呢?

POI是臭名昭着的内存饥饿,所以处理大型Excel文件时,内存不足并不罕见。

当你能够加载所有的原始文件,并且只是在编写合并文件时遇到麻烦的时候,你可以尝试使用SXSSFWorkbook而不是XSSFWorkbook并且在添加一定量的内容之后进行定期的刷新(参见poi- org.apache.poi.xssf.streaming的文档org.apache.poi.xssf.streaming )。 这样你就不必把整个生成的文件保存在内存中,只需要很小的一部分。

尝试分配更多的内存,例如。

 java -Xmx8192m 

另外你可以尝试一次合并一个xlsx文件,而不是一次加载它们。

您也可以将这一行移动到for循环中:

 excellFile1.close(); 

所以你马上closures它。

出现这个问题的原因如下

java.lang.OutOfMemoryError:GC开销限制超出的错误是JVM通知您应用程序花费太多时间进行垃圾回收的方式,结果太less。 默认情况下,如果JVM被configuration为在执行GC的总时间的98%以上,并且在GC之后只有不到2%的堆被恢复时引发此错误。

如果你只是想忽略这个问题,你可以设置下面的vm选项:

 -XX:-UseGCOverheadLimit 

有关更多信息,请参阅GC开销链接 。

您还可以使用下面的开关将更多的堆内存分配给您的应用程序。 在您的应用程序上运行一段时间的飞行员,并确定有多less内存会更适合您的应用程序

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

如果您可以避免使用方便但内存不足的工作簿API,则可以使用逐行处理数据的stream式处理逻辑来处理数据,这样的内存效率更高。

特别要特别注意使用:XSSFReader.SheetIterator来循环显示表单。

最后仔细看一下API的使用:XSSFSheetXMLHandler。 用于处理带有工作表的行。

查看这个项目的代码: https : //github.com/jeevatkm/excelReader/blob/master/src/main/java/com/myjeeva/poi/ExcelReader.java

您可以通过创build自己的方式来定义如何处理每一行:新的SheetContentsHandler ….

这很像SAXparsing,它不会占用你的内存。

  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)); }