从大表读取时的java.lang.OutOfMemoryError

时间:2012-06-09 05:18:46

标签: java jdbc out-of-memory

我正在尝试从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");也从未显示过。

  1. 我正在以autocommit模式运行。
  2. stmt.getResultSetConcurrency()返回1007。
  3. stmt.getResultSetHoldability()返回2.
  4. rs.getType()返回1003。

2 个答案:

答案 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);
      }
    }
  }