java - 将大量数据写入 excel : GC overhead limit exceeded

标签 java apache-poi

我有一个从 MongoDB 读取的字符串列表(~200k 行) 然后我想用Java代码将它写入excel文件:

public class OutputToExcelUtils {

    private static XSSFWorkbook workbook;
    private static final String DATA_SEPARATOR = "!";

    public static void clusterOutToExcel(List<String> data, String outputPath) {

        workbook = new XSSFWorkbook();
        FileOutputStream outputStream = null;

        writeData(data, "Data");


        try {
            outputStream = new FileOutputStream(outputPath);            
            workbook.write(outputStream);
            workbook.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void writeData(List<String> data, String sheetName) {

        int rowNum = 0;
        XSSFSheet sheet = workbook.getSheet(sheetName);     
        sheet = workbook.createSheet(sheetName);


        for (int i = 0; i < data.size(); i++) {
            System.out.println(sheetName + " Processing line: " + i);
            int colNum = 0;
            // Split into value of cell
            String[] valuesOfLine = data.get(i).split(DATA_SEPERATOR);

            Row row = sheet.createRow(rowNum++);

            for (String valueOfCell : valuesOfLine) {
                Cell cell = row.createCell(colNum++);
                cell.setCellValue(valueOfCell);
            }
        }
    }

}

然后我得到一个错误:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at org.apache.xmlbeans.impl.store.Cur$Locations.(Cur.java:497) at org.apache.xmlbeans.impl.store.Locale.(Locale.java:168) at org.apache.xmlbeans.impl.store.Locale.getLocale(Locale.java:242) at org.apache.xmlbeans.impl.store.Locale.newInstance(Locale.java:593) at org.apache.xmlbeans.impl.schema.SchemaTypeLoaderBase.newInstance(SchemaTypeLoaderBase.java:198) at org.apache.poi.POIXMLTypeLoader.newInstance(POIXMLTypeLoader.java:132) at org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst$Factory.newInstance(Unknown Source) at org.apache.poi.xssf.usermodel.XSSFRichTextString.(XSSFRichTextString.java:87) at org.apache.poi.xssf.usermodel.XSSFCell.setCellValue(XSSFCell.java:417) at ups.mongo.excelutil.OutputToExcelUtils.writeData(OutputToExcelUtils.java:80) at ups.mongo.excelutil.OutputToExcelUtils.clusterOutToExcel(OutputToExcelUtils.java:30) at ups.mongodb.App.main(App.java:74)

请给我一些建议?

谢谢你的尊重。

更新解决方案:使用 SXSSWorkbook 代替 XSSWorkbook

public class OutputToExcelUtils {

    private static SXSSFWorkbook workbook;
    private static final String DATA_SEPERATOR = "!";

    public static void clusterOutToExcel(ClusterOutput clusterObject, ClusterOutputTrade clusterOutputTrade,
            ClusterOutputDistance ClusterOutputDistance, String outputPath) {

        workbook = new SXSSFWorkbook();
        workbook.setCompressTempFiles(true);
        FileOutputStream outputStream = null;

        writeData(clusterOutputTrade.getTrades(), "Data");

        try {
            outputStream = new FileOutputStream(outputPath);            
            workbook.write(outputStream);
            workbook.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void writeData(List<String> data, String sheetName) {

        int rowNum = 0;
        SXSSFSheet sheet = workbook.createSheet(sheetName);
        sheet.setRandomAccessWindowSize(100); // For 100 rows saved in memory, it will flushed after wirtten to excel file

        for (int i = 0; i < data.size(); i++) {
            System.out.println(sheetName + " Processing line: " + i);
            int colNum = 0;
            // Split into value of cell
            String[] valuesOfLine = data.get(i).split(DATA_SEPERATOR);

            Row row = sheet.createRow(rowNum++);

            for (String valueOfCell : valuesOfLine) {
                Cell cell = row.createCell(colNum++);
                cell.setCellValue(valueOfCell);
            }
        }
    }

}

最佳答案

您的应用程序花费太多时间进行垃圾回收。这并不一定意味着堆空间用完了。但是,相对于执行实际工作,它在 GC 中花费的时间太多,因此 Java 运行时将其关闭。

尝试使用以下 JVM 选项启用吞吐量收集:

-XX:+UseParallelGC

当你这样做的时候,给你的应用程序尽可能多的堆空间:

-Xms????m

(其中 ???? 代表以 MB 为单位的堆空间量,例如 -Xms8192m)

如果这没有帮助,请尝试使用此选项设置更宽松的吞吐量目标:

-XX:GCTimeRatio=19 

这指定您的应用程序应该做比 GC 相关工作多 19 倍的有用工作,即它允许 GC 消耗最多 5% 的处理器时间(我相信更严格的 1% 默认目标可能会导致上述情况运行时错误)

不能保证他的意志会奏效。您能否查看并回复,以便其他遇到类似问题的人受益?

编辑

您的根本问题仍然是您需要在构建时将整个电子表格及其所有相关对象保存在内存中。另一种解决方案是序列化数据,即编写实际的电子表格文件而不是在内存中构建它并在最后保存它。但是,这需要阅读 XLXS 格式并创建自定义解决方案。

另一种选择是寻找一个内存密集度较低的库(如果存在的话)。 POI 的可能替代品是 JExcelAPI(开源)和 Aspose.Cells(商业)。

我几年前使用过 JExcelAPI 并获得了积极的体验(但是,它似乎没有 POI 积极维护,因此可能不再是最佳选择)。

编辑 2

看起来 POI 提供了一个流模型(https://poi.apache.org/spreadsheet/how-to.html#sxssf),所以这可能是最好的整体方法。

关于java - 将大量数据写入 excel : GC overhead limit exceeded,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49066977/

相关文章:

java - 我正在尝试使用 ServletConfig 接口(interface)加载我的 sql jdbc 驱动程序

java - 通过parse.com添加图片库

java - @RunWith(DataProvider.class) 时 @Before 不会被执行

java - 谷歌图表数据格式错误(javascript)

java - Spring Security OAuth2 - @EnableOauth2Sso 但也接受 token 作为身份验证

java - react excel 文件下载损坏

java - 使用 Apache POI 事件模型读取空单元格

image - 如何使用 apache poi 在 Excel 中集中对齐图像

java - 如何用java构建我们自己的分析器?

java - 读取 .pptx 导致 java.lang.ClassNotFoundException