すでに指摘したように、JIT(ジャストインタイム)コンパイラは、不要な反復を削除するために空のループを最適化できます。しかし、どうやって?
実際には、2つのJITコンパイラーC1およびC2があります。まず、コードはC1でコンパイルされます。C1は統計を収集し、JVMが100%の場合、空のループは何も変更せず、役に立たないことを発見するのに役立ちます。この状況では、C2がステージに入ります。コードが頻繁に呼び出される場合、収集された統計を使用して、コードをC2で最適化およびコンパイルできます。
例として、次のコードスニペットをテストします(私のJDKはslowdebug build 9-internalに設定されています)。
public class Demo {
private static void run() {
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
}
System.out.println("Done!");
}
}
次のコマンドラインオプションを使用します。
-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*Demo.run
そして、C1とC2で適切にコンパイルされた私のrunメソッドにはさまざまなバージョンがあります。私にとって、最終的なバリアント(C2)は次のようになります。
...
; B1: # B3 B2 <- BLOCK HEAD IS JUNK Freq: 1
0x00000000125461b0: mov dword ptr [rsp+0ffffffffffff7000h], eax
0x00000000125461b7: push rbp
0x00000000125461b8: sub rsp, 40h
0x00000000125461bc: mov ebp, dword ptr [rdx]
0x00000000125461be: mov rcx, rdx
0x00000000125461c1: mov r10, 57fbc220h
0x00000000125461cb: call indirect r10 ; *iload_1
0x00000000125461ce: cmp ebp, 7fffffffh ; 7fffffff => 2147483647
0x00000000125461d4: jnl 125461dbh ; jump if not less
; B2: # B3 <- B1 Freq: 0.999999
0x00000000125461d6: mov ebp, 7fffffffh ; *if_icmpge
; B3: # N44 <- B1 B2 Freq: 1
0x00000000125461db: mov edx, 0ffffff5dh
0x0000000012837d60: nop
0x0000000012837d61: nop
0x0000000012837d62: nop
0x0000000012837d63: call 0ae86fa0h
...
少し面倒ですが、よく見ると、ここには長い実行ループがないことがわかります。B1、B2、B3の3つのブロックがあり、実行ステップはB1 -> B2 -> B3
またはB1 -> B3
です。Where- Freq: 1
ブロック実行の正規化された推定頻度。
javap -v
を調べて確認してください。