繁体   English   中英

Apache POI 超出了 GC 开销限制

[英]GC overhead limit exceeded with Apache POI

我有 13 个 .xlsx 文件,每个文件大约有 1000 行。 现在我想用一张纸将它合并到一个 .xlsx 文件中。 我正在使用https://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();
    }

所有文件都在加载和合并,但在“写入”系统输出后我得到

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并在添加一定数量的内容后进行定期刷新(请参阅org.apache.poi.xssf.streaming poi 文档org.apache.poi.xssf.streaming )。 这样您就不必将整个生成的文件保存在内存中,而只保存一小部分。

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

java -Xmx8192m

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

您还可以将此行移动到您的 for 循环中:

excellFile1.close();

所以你马上关闭它。

出现此问题是由于以下原因

java.lang.OutOfMemoryError:GC 开销限制超出错误是 JVM 发出信号的方式,表明您的应用程序花费太多时间进行垃圾收集而结果太少。 默认情况下,如果 JVM 花费超过 98% 的总时间进行 GC 并且在 GC 之后仅恢复不到 2% 的堆时,JVM 被配置为抛出此错误。

如果您只想忽略这个问题,您可以设置以下虚拟机选项:

-XX:-UseGCOverheadLimit

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

您还可以使用以下开关为您的应用程序分配更多堆内存。 在您的应用程序上运行一段时间并确定多少内存对您的应用程序更好

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

如果您可以避免使用方便但占用大量内存的工作簿 API,请改用逐行处理数据的流逻辑,这样可以提高内存效率。

特别要注意使用: XSSFReader.SheetIterator 来循环工作表。

最后好好看看API的用法:XSSFSheetXMLHandler。 用于处理带有工作表的行。

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

您可以通过创建自己的方式定义处理每一行的方式:new 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)); }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM