BufferedWriter写入多行并导致OutOfMemory错误

时间:2018-02-20 11:41:54

标签: java bufferedwriter

我有一个类从数据库中获取一个非常大的结果集,大约10,000,000行,我需要写入文件。我有一个输出每10k行的记录器,但是当它达到数百万行时,它开始以指数方式减慢,直到我得到java.lang.OutOfMemoryError: GC overhead limit exceeded错误。我也将-Xmx6000m作为VM的参数传递。

我可以使用任何方法来防止上述错误吗?使用try with resources方法不是最好的方法吗?我应该使用缓冲写入器以外的方法吗?

File outputFile = new File(incoming.toUri());
StringBuilder outputCSV = new StringBuilder();
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(outputFile))) {
    for (RowIterator rowIterator = tradesFromKDB.rowIterator(); rowIterator.hasNext(); count++) {
        outputCSV.setLength(0);
        row = rowIterator.next();

        for (String cell : row.toString().split("\\|")) {
            try {
                outputCSV.append(cell.split("=")[1] + ",");
            } catch (ArrayIndexOutOfBoundsException e){
                // This is a result of the right hand side of an = not having a value
                // E.G. |consolidatedflag=|
                // In such a case a comma is just appended
                outputCSV.append(',');
            }
        }

        if( count % 10000 == 0)
            logger.info("Processed " + count + " rows.");

        // Remove the last to characters; the ending '>' and trailing comma
        outputCSV.setLength(outputCSV.length() - 2);
        outputCSV.append("\n");

        bufferedWriter.write(outputCSV.toString());
    }
} catch (IOException e) {
    logger.error(e.getMessage());
}

修改

我已将其删除以删除大量垃圾以便写下该行:

File outputFile = new File(incomingQPBEOD.toUri());
StringBuilder outputCSV = new StringBuilder();
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(outputFile))) {
    for (RowIterator rowIterator = tradesFromKDB.rowIterator(); rowIterator.hasNext(); count++) {
        row = rowIterator.next();

        if( count % 10000 == 0)
            logger.info("Processed " + count + " rows.");

        bufferedWriter.write(row.toString());
    }
} catch (IOException e) {
    logger.error(e.getMessage());
}

问题仍然存在,因此它是rowIterator或bufferWriter。

编辑2

我已删除了对BuffererWriter的写入内容,并且我没有遇到内存问题。

        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(outputFile))) {
            for (RowIterator rowIterator = tradesFromKDB.rowIterator(); rowIterator.hasNext(); count++) {
                row = rowIterator.next();

                if( count % 10000 == 0)
                    logger.info("Processed " + count + " rows.");

//              bufferedWriter.write(row.toString());
            }
        } catch (IOException e) {
            logger.error(e.getMessage());
        }

编辑3

返回的数据总是非常相似:

处理了990000行。

<Row:989999 date=2018-02-14|time=09:21:01|sym=TEST|price=1000.00|size=0|ex=MSCI|ttype=NONE|execvenue=NONE|extime=Wed Jul 14 09:24:01 GMT 2017|gmdtime=Wed Jul 14 09:21:01 GMT 2017|consolidated_flag=flagtype|trade_flags=Num|extimeNS=2018-02-14 09:21:01.261320525>

它总是拥有那么多的密钥对值。

如果我将row.toString()的值分配给变量,那么问题就出现了。

将下面的字符串分配给字符串会导致速度减慢和崩溃,但只是将toString()值打印到记录器不会导致任何减速。

try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(outputFile))) {
            for (RowIterator rowIterator = tradesFromKDB.rowIterator(); rowIterator.hasNext(); count++) {
                Row row = rowIterator.next();

                if( count % 10000 == 0) {
                    logger.info("Processed " + count + " rows.");
                    logger.info(row.toString());
                }

                // declared outside try with resource
                s = row.toString();
//              bufferedWriter.write(row.toString());
            }
        } catch (IOException e) {
            logger.error(e.getMessage());
        }

1 个答案:

答案 0 :(得分:0)

问题是垃圾收集无法有效地恢复堆(意味着98%的CPU时间用于恢复少于2%的堆)。

这可能是因为数据量巨大。通常,只要请求的缓冲区长度超过缓冲区大小,BufferedWriter就应该刷新到底层流。

虽然不能保证Buffer的问题,它也可能是数据库连接,你应该像GPI建议的那样一步一步地调试它。

这是一个非常有趣的问题,我从未遇到过。您可能尝试在本地拆分文件,但可能没有这么大的结果?如果这不可行,那么可能有时关闭流并重新打开可以通过使GC以这种方式恢复更多堆来解决问题。