home wiki.fukuchiharuki.me
Menu

キーワード

  • MySQL
  • PreparedStatement
  • ResultSet
  • OutOfMemoryError

現象

一行ずつResultSetからデータ取得しているはずなのにOutOfMemoryErrorになる。

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at com.mysql.jdbc.MysqlIO.nextRowFast(MysqlIO.java:1649)
	at com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:1426)
	at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:2924)
	at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:477)
	at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:2619)
	at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:1788)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2209)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2625)
	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2119)
	at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2281)

原因

すべての行をfetchしてResultSetを生成しようとするようです。なので、取得対象が膨大になるとすぐにOutOfMemoryErrorを起こします。

一行ずつ取得するようにした実装が泣くね。

対策

一定行ずつfetchするようにMySQLを設定します。

接続プロパティで指定する方法

次のクエリストリングを接続プロパティに加えます。

useCursorFetch=true&defaultFetchSize=100

100は一度にfetchする行数のよう。なので様子みて適宜。

コードで指定する方法

stmt = conn.createStatement(
         java.sql.ResultSet.TYPE_FORWARD_ONLY, 
         java.sql.ResultSet.CONCUR_READ_ONLY
       );
stmt.setFetchSize(Integer.MIN_VALUE);

備考

PreparedStetementもデフォルトではPreparedしてないというとんでも罠があったりするの注意。

参考