障害メモ/PreparedStatementがOutOfMemoryErrorを起こす
キーワード
- 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してないというとんでも罠があったりするの注意。
参考
作成日 2015-08-11
