HOWTO · Java

Javaインライン関数

Javaにはソースレベルのinline宣言がありません。この記事では、JVMによるメソッドのインライン化の可能性と、メソッド呼び出しを同等の文と比較するJavaの例を解説します。

このページの内容

Javaには、ソースレベルで宣言するinline関数はありません。「Javaのインライン関数」を調べる開発者が通常指しているのはメソッドのインライン化です。これは、JVMがメソッド呼び出しをそのメソッドの命令に置き換えることがある実行時最適化です。Javaでは通常のメソッドを記述し、プログラムの実行中に呼び出しをインライン化するかどうかはJVMが判断します。

Javaにおけるメソッドのインライン化とは?

通常、メソッドを呼び出すと、そのメソッドに処理が移り、結果が返されます。インライン化では、コンパイル後の機械語で呼び出し箇所にメソッドの命令を配置し、呼び出しを取り除くことがあります。これはJVMによる最適化であり、Javaソースを書き換えることでも、自分でコードをコピーして行うことでもありません。Javaコンパイラは引き続きソースをクラスファイルにコンパイルし、実行時の処理と最適化はJVMが担います。

インライン化は、すべてのメソッドがあらゆる場所に展開されるという意味ではありません。実行環境はプログラムの動作を保つ必要があり、インライン化が適さない場合は呼び出しをそのまま残すことがあります。メソッドは分かりやすく保ち、この実装上の判断はJVMに任せましょう。

HotSpot JVMによるメソッドのインライン化

HotSpotのジャストインタイム(JIT)コンパイラは、実行時最適化の一環として、条件に適したメソッド呼び出しをインライン化することがあります。その判断はコードや実行時の条件によって異なり、Java言語で保証されている動作ではありません。そのため、すべてのJVMでインライン化が常に有効だと説明したり、この例を動かすために実装固有のフラグを前提にしたりしないでください。以下の例が示すのは計算結果が等価であることです。HotSpotが内部で何をコンパイルするかを制御したり、明らかにしたりするものではありません。

メソッド呼び出しと同等の文を比較するJavaの例を作成する

最初の計算ではaddBonusを呼び出し、その後にscoreを呼び出します。2つ目の計算では、同じ算術処理を直接行います。どちらも同じ値から始めるため、JVMが実際にどちらかの呼び出しをインライン化したと主張せずに、結果を比較できます。

次の例を実行してください。コード内の表示ラベルはバイト単位で保持されているため、英語のままです。

public class Main {
    static int addBonus(int score) {
        return score + 2;
    }

    static int score(int value) {
        return value * 3;
    }

    public static void main(String[] args) {
        int byMethodCalls = score(addBonus(4));

        int value = 4;
        int byStatements = (value + 2) * 3;

        System.out.println("Method calls: " + byMethodCalls);
        System.out.println("Direct statements: " + byStatements);
    }
}
Method calls: 18
Direct statements: 18

例をコンパイルして実行する

ソースを空の作業ディレクトリにMain.javaという名前で保存します。そのディレクトリで、JDKがPATHに設定されている状態でコンパイルして実行してください。

そのディレクトリでjavac Main.javaを実行し、続けてjava Mainを実行します。これらのコマンドは上記のクラス名とファイル名に対応しており、JVMのチューニングオプションは必要ありません。この例の実行では正常に終了し、上に示された出力が表示されました。

結果から分かること、分からないこと

プログラムを実行したら、表示される2つの値を比較してください。メソッド呼び出しと直接記述した式は同じ算術処理を行うため、値は一致するはずです。この小さな例で確認できるのは、結果が等価であることです。JVMがどちらかのメソッドをインライン化したかどうかは分かりません。また、値が一致しても、どちらの形式が速く実行されるかは分かりません。性能を測定するには、別途、条件をそろえたベンチマークが必要です。読みやすいJavaソースを書くために通常のメソッドを使い、インライン化は宣言できる機能やこの出力から推測できる性能上の結果ではなく、実行時に行われる可能性のある最適化として捉えてください。