我正在尝试从PostgreSQL中的一个非常大的表(300GB)预生成报告。我做这样的事情:
rs = stmt.executeQuery("SELECT * FROM tbl");
System.out.println("select all finished");
while (rs.next()) {
/* generate report and save it in report table */
/* generated reports are not in memory,
* They are saved in a summary table in each iteration */
}
当我启动应用程序时,它会给出Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
。我尝试使用stmt.setFetchSize(1000)
但它没有解决问题。
解决方案是什么?我在Debian 6.0.5和openJDK 6上使用PostgreSQL 8.4.11。
[UPDATE]
打印的堆栈跟踪显示OutOfMemoryError
行已生成rs = stmt.executeQuery("SELECT * FROM tbl");
异常。 System.out.println("select all finished");
也从未显示过。
autocommit
模式运行。stmt.getResultSetConcurrency()
返回1007。stmt.getResultSetHoldability()
返回2. rs.getType()
返回1003。答案 0 :(得分:8)
问题可能是PostgreSQL仅在一小部分情况下使用fetchSize
。请参阅:http://jdbc.postgresql.org/documentation/91/query.html#fetchsize-example
- 与服务器的连接必须使用V3协议。这是服务器版本7.4及更高版本的默认设置(仅支持)。
- 连接不得处于自动提交模式。后端在事务结束时关闭游标,因此在自动提交模式下,后端将关闭游标,然后才能从中获取任何内容。
- 必须使用ResultSet类型ResultSet.TYPE_FORWARD_ONLY创建Statement。这是默认值,因此不需要重写代码以利用此功能,但这也意味着您无法向后滚动或以其他方式在ResultSet中跳转。
- 给出的查询必须是单个语句,而不是与分号串在一起的多个语句。
因此,如果您在自动提交中执行此操作,或者使用除TYPE_FORWARD_ONLY
之外的结果集类型,PostgreSQL将获取所有行。另外看一下PostgreSQL JDBC 9.0-801驱动程序的来源,看起来使用一个可保持的结果集也会使它获取所有行。
答案 1 :(得分:0)
我不认为你有什么会导致那种错误。我相信垃圾收集器会在您遍历rs.next()
时出现,因此您不应该遇到内存问题。它可能与您尝试对结果集做的事情有关。虽然不知道你正在做什么,但我只能猜测你是想把所有东西存放在内存中的一个对象中。因此,如果您将值存储到StringBuilder
或某些问题中。我建议您在结束时将结果写入磁盘,而不是尝试将其全部收集到内存中的对象中(同样,我只是在猜测您正在做什么,因为您没有提供这种信息)。在Java Helper Library中,您可能会发现resultSetToCSVFile(ResultSet rs, String destination)
方法很有帮助。以这种方式处理它会阻止您将其全部保存在内存中,但您可以编写您正在尝试的报告。顺便说一句,为此,您需要包含opencsv library。或者您可以直接通过包含Java Helper Library。
/**
* Prints the given ResultSet to a comma separated file (the destination)
*
* @param rs
* @param destination
* @throws SQLException
* @throws FileNotFoundException
*/
public static void resultSetToCSVFile(ResultSet rs, String destination) throws SQLException, FileNotFoundException, IOException {
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
String[] header = new String[columnCount];
for (int i = 0; i < columnCount; i++) {
header[i] = metaData.getColumnName(i + 1);
}
File file = new File(destination);
IOHelper.checkDirectory(file);
try (PrintWriter pw = new PrintWriter(file); CSVWriter writer = new CSVWriter(pw)) {
writer.writeNext(header);
while (rs.next()) {
String[] row = new String[columnCount];
for (int i = 0; i < columnCount; i++) {
String string = rs.getString(i + 1);
if (string == null) {
string = "";
}
row[i] = string;
}
writer.writeNext(row);
}
}
}