Java言語でシステム開発を行っていると、作成したプログラムの処理が遅く実行環境やJava言語自体の処理が遅いのではないかと疑ってしまうこともあります。
しかしパフォーマンス問題の要因は、開発者自身のプログラムの書き方にあることも少なくありません。
本記事では、Javaのパフォーマンスを改善するために意識しておきたいポイントについて、プログラミング初心者向けに基本を解説していきます。
目次
Javaプログラムをチェックする前にパフォーマンス・チューニングの基礎知識
パフォーマンス・チューニングを行う前に、Javaのコード以外の部分にも目を向けた上で、影響範囲を意識して作業を実施する必要があります。
システムのチューニングに関わる様々な要因
Javaで開発されたシステムのパフォーマンスが悪いといっても、必ずしもJavaプログラムに原因があるとは限りません。
システムを動かす上で、サーバーの状態やネットワークの遅延、データベースへの過負荷などプログラム以外の様々な要因を見極めた上で調査を実施する必要があります。
調査時の影響
システムの調査を行う上で、本番稼働しているシステムでは特にですが、実際のシステムに掛かる負担も考えた上で調査方法を検討する必要があります。
パフォーマンス・チューニングは多くの場合、最低限動いているシステムに対して何らかの調査を行うことになるため、調査による負荷で元のシステムを止めてしまっては意味がありません。
調査対象のシステムがどういった稼働状況で、どれくらいまで負荷の高いツールを導入出来るのか、テストをすることが出来るのか検討することは大切です。
チューニングによる影響
パフォーマンス・チューニングを行った際、問題となっているボトルネックが解消出来たとしても他の処理に影響が出るようでは十分とは言えません。
システム全体をしっかりと確認した上で、他の処理に影響がないのか、影響がある場合には許容範囲なのかをしっかりと確認する必要があります。
パフォーマンスのボトルネックとなっているJavaプログラムを探す
Javaのパフォーマンスを改善するには、まず処理に時間が掛かっているプログラムを探すことが第一です。
データベースアクセスを調査
Javaのシステムでボトルネックとなりやすいのは、データベースへのアクセス処理です。
データベースを使って大量のデータを抽出する際など、どうしても時間の掛かる処理が多くなるため、SQLのパフォーマンスを調べるのが先決です。
新規でのシステム開発であればデータベース設計を見直すことを含め、SQL文をチューニングして実行速度を改善することが、大幅にパフォーマンスを改善するための最初に検討すべきポイントです。
ブレークポイントで目星を付ける
データベースアクセス以外にボトルネックとなりそうなプログラムがある場合、まずはデバッグ機能のブレークポイントを使って大まかに処理の目星を付けましょう。
ブレークポイントをいくつも設定しておき、あきらかに処理の中断までに時間が掛かっている部分を見つけることで、大まかな原因の特定は可能です。
実行時間を計測して処理を特定
大まかにプログラムでボトルネックとなっている場所が判明したら、実際に実行時間を測ってより詳細に問題箇所を特定します。
やり方は様々ですが、例としては下記のように「System.currentTimeMillis()」を使ってメソッドの実行時間を計測するなどが可能です。
public class Main { public static void main(String[] args) { System.out.println("開始:" + System.currentTimeMillis()); // メソッド呼び出し System.out.println("終了:" + System.currentTimeMillis()); } }
開始と終了時間を測ることで、実行時間を計算することが出来ます。
Javaのパフォーマンスを改善するためのポイント
Javaのコーディングでパフォーマンスを改善するために、比較的有名な基本パターンをいくつか紹介していきたいと思います。
ループの条件式はシンプルに
プログラミングを行う際、ループ処理は必須処理といえるほど頻繁に登場する処理ですが、ループの条件式に複雑な式を含めることはパフォーマンスの低下に繋がります。
ループ条件は繰り返しのたびに計算が行われるため、ループ条件の値を固定してしまった方が処理速度は向上します。
List list = new ArrayList(); for(int i=0; i<list.size(); i++) { // 処理 }
サンプルでは「list.size()」が複雑な条件式に当たるため、下記のように修正することで処理速度の向上が期待出来ます。
List list = new ArrayList(); for(int i=0, size =list.size();i < size; i++) { // 処理 }
あらかじめ変数設定の際に「list.size()」式の結果を変数に格納しておき、条件式には変数を利用するように修正しています。
final修飾子を利用する
final修飾子は、クラスやメソッドをオーバーライドすることが出来るなくなる修飾子です。
つまりfinal修飾子が付いている場合、クラスやメソッドがこれ以上変更されることがないため、コンパイラとしては効率的にJavaバイトコードを生成出来るわけです。
ちなみにクラスにfinal修飾子を付けると、クラス内全てのメソッドがオーバーライド出来なくなるため、パフォーマンス向上がより期待出来ます。
無駄なインスタンス生成は避ける
不要な部分でのインスタンス生成を避けることにより、システム全体で見るとパフォーマンス改善に繋がることもあります。
例えばif文の中でしか使わないインスタンスであれば、if文の外でインスタンス化するよりも条件に一致した場合のみインスタンス化した方が効率的です。
インスタンス化 if(条件式) { インスタンスを使った処理 }
下記に修正した方がパフォーマンスは向上する。
if(条件式) { インスタンス化 インスタンスを使った処理 }
シフト演算を使う
乗算・除算の処理では「*」「/」を使用するよりも「<<」「>>」を利用した方が処理スピードが向上します。
一般的な乗算・除算で記述した場合
int num = 10 * 2; int num = 10 / 2;
シフト演算に修正した場合
int num = 10 << 2; // 乗算 int num = 10 >> 2; // 除算
静的変数は必要な場合にしか使わない
静的変数としてオブジェクトを定義した場合、ガベージコレクターによりオブジェクトが不要と判断されず、メモリが解放されません。
静的オブジェクトの変数はクラスとライフライクルが同じになってしまうため、利用していなくても無駄にメモリを確保し続けることになってしまいます。
public class クラス名 { public static クラス名 インスタンス = new クラス名(); }
さいごに: Javaのパフォーマンスを最適化出来るよう意識してコードを書こう
本記事では、Javaプログラムのパフォーマンス・チューニングに関する基本知識をご紹介してきました。
システム全体のチューニングをするには、様々な要因を調査することが大切ですが、開発者が意識してコードを記述するだけでも大きくパフォーマンスが向上することも少なくありません。
最近はコンピューターのマシンスペックも上がっているため、あまり意識する必要がない部分も増えてきてはいますが、よりパフォーマンスの高いシステムを作るためにも、効率的なコードが書けるように知識を増やしていきましょう。