执行数百万个查询时出现java.lang.OutOfMemoryError: Java堆空间错误

3
在我的应用程序中,我需要对MySQL数据库执行数百万个查询。代码如下:
for (int i=0; i< num_rows ; i++) {
   String query2="select id from mytable where x='"+ y.get(i) "'";              
   Statement stmt2 = Con0.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);     
   ResultSet rs2 = stmt2.executeQuery(query2);    
   ... // process result in rs2
   rs2.close(); 
}

其中num_rows大约为200万。在经过60万次循环后,Java报告了一个错误并退出:

java.lang.OutOfMemoryError:Java堆空间错误。

我的代码有什么问题?我应该如何避免这样的错误?

提前感谢您!


1
你在这里具体做什么与数据有关? - hvgotcodes
SQL数据库对数百万次查询的处理效率并不高,因为每个查询都有相对较高的开销。最好将查询批量分成块,比如100个查询一组。http://www.google.com/search?q=jdbc+batching+performance - Peter Lawrey
5个回答

5

同时也要关闭你的语句。


4

这里不适合使用语句。请尝试以下代码:

PreparedStatement pre = Con0.prepareStatement("select id from mytable where x=?");

for (int i=0; i< num_rows ; i++) {
   pre.setString(1, y.get(i));
   ResultSet rs2 = pre.executeQuery();
   ... // process result in rs2
   rs2.close(); 
   pre.clearParameters();
}
pre.close();

2
我不知道你所接受的答案是否解决了你的问题,因为它并没有改变可能导致问题的任何内容。
问题在于当ResultSet缓存查询返回的所有行时,这些行可以在你遍历集合时存储或预取。我曾经遇到过类似的问题,使用PostgreSQL JDBC驱动程序时,在非事务模式下运行时忽略了游标获取大小。
JDBC驱动程序应该对这样的查询使用游标,所以你应该查看驱动程序文档中的fetchSize参数。作为替代方案,你可以通过执行SQL命令来创建游标并获取下一个X行来自己管理游标。

1

假设你正在使用单个连接来执行所有查询,并且你的代码比你展示的更复杂,那么重要的是确保每个 Statement 和 ResultSet 在使用完毕后都关闭。这意味着你需要像这样使用 try/finally 块:

for (int i=0; i< num_rows ; i++) {
  String query2="select id from mytable where x='"+ y.get(i) "'";              
  Statement stmt2 = Con0.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);     
  ResultSet rs2 = null;   
  try {
    rs2 = stmt2.executeQuery(query2);    
    ... // process result in rs2
  } finally {
    try {
      stmt2.close();
    } catch (SQLException sqle) {
      // complain to logs
    }
    try {
      if (rs2 != null) { rs2.close(); }
    } catch (SQLException sqle) {
    }
  }
}

如果您不积极且果断地关闭所有结果集和语句对象,并且如果您请求得足够快,那么您将耗尽内存。


1

使用prepared statement,由于每次循环中只有X的值会更改,因此在循环之外声明应该有所帮助。另外,在显示的代码中,你没有关闭使用的语句,这可能不利于垃圾收集器释放已使用的内存。


我采用了约翰和安吉洛的方法,它非常有效。也感谢其他的人。 - Xiaodong Yu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接