Java | x = x++ の内部的な挙動を JVM レベルで詳しく知る

Java Java
スポンサーリンク

x = x++; は一見「xを1増やして代入している」ように見えますが、JVMレベルのバイトコードの動きを追うと「結局値が変わらない」という挙動になります。内部的にどう処理されているかを詳しく見ていきましょう。


1. ソースコード

public class Sample {
    public static void main(String[] args) {
        int x = 0;
        x = x++;
        System.out.println(x);
    }
}
Java

2. コンパイル後のバイトコード(javap -c Sample の一部)

0: iconst_0        // 定数0をスタックに積む
1: istore_1        // x = 0 をローカル変数1に格納
2: iload_1         // xの値をスタックに積む(ここで0)
3: iinc 1, 1       // ローカル変数1 (x) をインクリメント(x=1になる)
6: istore_1        // スタックトップの値(0)をxに代入 → xが再び0に上書きされる
7: getstatic ...
10: iload_1
11: invokevirtual ...

3. スタックとローカル変数の動き

  • ステップ 2 (iload_1)
    ローカル変数 x の値(0)をスタックに積む。
    → スタック: [0], ローカル変数: x=0
  • ステップ 3 (iinc 1, 1)
    ローカル変数 x を直接インクリメント。
    → スタック: [0], ローカル変数: x=1
  • ステップ 6 (istore_1)
    スタックトップの値(0)をローカル変数 x に格納。
    → スタック: [], ローカル変数: x=0

結果として、インクリメントされた値は捨てられ、元の値が代入されるため、x は変化しません。


4. なぜこうなるのか?

  • x++ は「元の値を返しつつ、変数を1増やす」という式。
  • x = x++; の場合、右辺の「元の値」が左辺に代入されるため、インクリメントの効果が消える。
  • JVMバイトコード的には「スタックに古い値を積む → ローカル変数を+1 → スタックの古い値を再び代入」という順序になる。

5. 実用的なまとめ

  • x = x++;無意味なコード(結果的に x は変わらない)。
  • これは「副作用を持つ式を代入に使う」典型的な落とし穴。
  • 実際のコードでは避けるべきで、x++;x = x + 1; を明示的に書く方が安全。

6. 発展的な話

  • iinc 命令は「ローカル変数を直接インクリメントする」特殊命令で、スタックを使わない。
  • そのため「スタックに積んだ古い値」と「ローカル変数の更新」がズレてしまい、最後に古い値で上書きされる。
  • これは Java言語仕様 (JLS) に沿った正しい動作であり、コンパイラが意図的にこういうバイトコードを生成している。
タイトルとURLをコピーしました