MySQL

JDBC ドライバ

OutOfMemoryError

MySQL の JDBC ドライバのデフォルトの設定で, 行数の多いテーブルからデータを取得すると OutOfMemoryError (OOME) で落ちることがある. これは java.sql.ResultSet オブジェクトが, テーブルの行数分だけオブジェクトを保持してしまっているから.

MySQL の場合, java.sql.Statement#setFetchSize を使って fetchSize を小さめの値 (10 など) に設定しても, このエラーは解消されない. fetchSize はあくまで,「1 回の DB アクセスで何行のデータを持ってくるかのヒント」でしかないからだ. 残念ながら, ResultSet オブジェクトを大量に抱えてしまうことには変わりない.

MySQL では以下の設定を行うことで ResultSet を全て抱えるのを回避できる. この設定では,「データを 1 行読んでは捨てる」ような動作になるらしい. 実際にはある程度のバッファを持ってはいるだろうが, 実際に試したところメモリ使用量はある一定以上に行くことは無かった.

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

(公式ドキュメント より引用)

因みに,「fetchSize に負の数を与えたときは java.sql.SQLException を投げる」と Statement インターフェースでは規定されているが, MySQL の実装クラスでは華麗にその仕様を破っている.

この設定にしたときの制約条件として,

  • データを読んでいる途中で他のクエリを発行してはいけない

というものがある.

参考::