TMD45'β'LOG!!!

Life is Beta-ful.

Java の性能改善メモ

Java プログラムの性能改善(ここでは処理速度の改善)を行ったので、その方法のメモです。
ちなみに自分の場合、以下の方法で処理速度が元の 15~36 倍短くなりました。なんじゃそら~ヘ(゚∀゚ヘ)アヒャ*1

ネストされたDBアクセス、ファイルアクセスを減らす

性能改善というか、もともとこれがひどいコードで。。。

改善ポイントとしてはまず無駄な I/O アクセスを減らすのが一番効果があります。外部通信というのはそれだけで時間がかかるもので、逆に Java 内の処理であれば基本的にそれほど時間を要するものではありません。

DBアクセスの場合、ループ内で何度も検索を行なっていましたが、ループの度に取得される結果が変わるわけでもなく、更新するわけでもなく…そんな感じだったので、ループの外側で取得した検索結果を使いまわすように変更。

また、ファイル操作については「Connection Open → ファイル操作 → Connection Close」という一連の操作がループ内で行われている場合も改善出来るなら改善したいところです。Close 漏れを防ぐために習慣として Open したメソッド内で必ず Close するようになってたりすると、そのメソッドがひどいネストのループ内で小刻みに呼び出されていたりして。。。

ループのネストについては、ネストによる操作が必要になるようなDB構造にも問題があるのですが、稼働中のプログラムに改善を施すのは難しい場合もあります…(´・ω・`)

ArrayList を HashSet に置き換え可能なら置き換える

テーブルカラムに合わせた独自クラス(HogeBean)を用意しておいて、List<HogeBean> としてメモリ上に保持していました。が、検索結果を順列に保持したり、リスト中から1つを抽出したりという操作が無かったので HashSet を使うように変更 Set<HogeBean>。これでもっと高速化&メモリ節約が望めます。

ただし List(ArrayList)と Set(HashSet)の違いには注意です。

参考:

PreparedStatementでフェッチサイズを指定する

大量データの検索時には java.sql.Statement#setFetchSize(int rows) でフェッチサイズを指定してやると、高速化が見込める場合があります。java.sql.ResultSet#setFetchSize(int rows) でも同様。ただしこの値(fetchSize)は、あくまでJDBCに与える「参考値」であるため、場合によっては期待する結果にならないこともあるかも。

実体験として JDK 1.5.0_12, OJDBC14 では効果が見られました。
設定する値はデフォルト値(Oracle JDBC で 10 らしい)より大きければいくらでも良さそうな感じで、上記記事の結果からは 1000~10000 程度が良さそうです。取得するレコードの件数から厳密に計算する必要も無いようで、自分の例では「扱うテーブルの全レコード数が数千レコード(5千くらいだったかな…)」で「検索結果が数レコード」という場合に fetchSize に 100~1000 指定するという感じで使いましたが、それでも fetchSize を指定していないときよりは早くなるようでした。
取得できるレコード数より多い値を指定しても SQLException にはなりません。その辺はドキュメントを確認してみてください。

参考:

速度改善の注意点

I/O アクセスを減らすということは、(今回の方法でいうと)1回に取得するデータを増やしてそれをメモリ上に保持しておき、データの取得をメモリ上で済ませるようにするということです。上記の方法で処理速度は上がりますが、代わりにメモリ使用量が増えます。メモリ不足などを原因として性能改善を考える場合は、むやみに今回の方法を使ってはいかんです。

今回メモしたそれぞれの方法については、それぞれのキーワードでググると詳細な情報が出てくると思いますので、ご利用の際には別途調べてみてください(放り投げ)。

*1:普通そこまで早くなるなんて無いと思うんですけど…元のコードが酷すぎただけや…

▲ ページトップへ移動