Oracle DB のテーブルに格納されたレコードのうち、SELECT した数十万レコード程度を ResultSet 経由で受け取るようにしていたところ、ResultSet#next を呼び出すループを回す毎に JVM のメモリが増えていく事案が発生。ヒープをあまり取っていないと、メモリ不足で奈落へと落ちていく。ただ深く。ひたすら深く。
Javadoc 的には
A ResultSet object maintains a cursor pointing to its current row of data.
ResultSet (Java Platform SE 6)
ということなので、メモリは食わないかなと思っていたのだけれど。
調べていくと、Oracle の JDBC 実装によるものと判明。ResultSet にスクロール可能と指定していると、ResultSet はメモリを喰って成長し、自重に耐えきれずに崩壊する。
基礎となるサーバーがスクロール可能なカーソルをサポートしていないので、スクロール可能性は別のレイヤーでOracle JDBCによって実装する必要があります。この機能は、スクロール可能な結果セットの行をクライアント側のメモリー・キャッシュに格納することにより、実現されていることに注意してください。
重要:
http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/java.111/E05720-02/resltset.htm
スクロール可能な結果セットの行はすべて、クライアント側のキャッシュに格納されます。そのため、結果セットに多くの行、多くの列または非常に大きな列が含まれていると、クライアント側のJava Virtual Machine(JVM)に障害が発生する可能性があります。大きな結果セットにはスクロール可能性を指定しないでください。
成長を見守るの、まるで楽しくない。