【发布时间】:2018-02-20 11:41:53
【问题描述】:
我有一个类可以从数据库中获取一个非常大的结果集,大约 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() 的值分配给变量,问题似乎就来了。
将以下内容分配给 String 会导致减速和崩溃,但简单地将 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());
}
【问题讨论】:
-
尝试增加 jvm 堆大小。
-
你在初始化 BufferedWriter 对象时尝试过使用缓冲区大小
-
为问题添加了缓冲区信息;
-Xmx6000m是我通过的 -
为什么说是缓冲写入器导致OOM。您在这里发生了一些事情:数据库连接(如果它缓存行,就像许多 JDBC 驱动程序和/或 ORM 所做的那样),这可能是真正的原因。它可能是缓冲区(尽管您似乎重置了它),也可能是单行太大...逐个测试和/或配置文件。
-
谢谢。我认为你甚至可以在没有作者的情况下添加一个版本,看看会发生什么。
标签: java bufferedwriter