我有一个类从数据库中获取一个非常大的结果集,大约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());
}
答案 0 :(得分:0)
问题是垃圾收集无法有效地恢复堆(意味着98%的CPU时间用于恢复少于2%的堆)。
这可能是因为数据量巨大。通常,只要请求的缓冲区长度超过缓冲区大小,BufferedWriter就应该刷新到底层流。
虽然不能保证Buffer的问题,它也可能是数据库连接,你应该像GPI建议的那样一步一步地调试它。
这是一个非常有趣的问题,我从未遇到过。您可能尝试在本地拆分文件,但可能没有这么大的结果?如果这不可行,那么可能有时关闭流并重新打开可以通过使GC以这种方式恢复更多堆来解决问题。