JIT コンパイラ関連オプションの挙動

Oracle Java 6 での JIT コンパイラ関連のオプションとその挙動についての記述。とはいえ大きく挙動を変えられるような実装ではありませんので以下の 3 つを押さえておけば良いかと思います。

-XX:CompileThreshold=10000 JIT コンパイルを行うまでのメソッド呼び出し回数または分岐回数
-XX:+CITime プログラム終了時に JIT コンパイラの実行統計を出力 (JDK 1.4.0 以降)
-XX:+PrintCompilation JIT コンパイル実行時にメッセージを出力

他に -Xint (JIT コンパイルを行わない), -Xbatch (フォアグラウンドで JIT コンパイルを行う) オプションもありますが、これらは JIT コンパイラの実装が貧弱で混沌としていた時代の名残です。今時は興味実行以外での利用機会もないので無視します。

実行サンプルに末尾再帰でフィボナッチ数 F_{(10000)} を求めるプログラムについて。メソッド fibonacci() は引数 n\geq 0 に対して合計値がそのフィボナッチ数となる 2 つの要素を返します。

import java.math.*;
public class A{
  private static final BigDecimal[] fibonacci(int n){
    if(n == 0){
      return new BigDecimal[]{ BigDecimal.ZERO, BigDecimal.ZERO };
    }
    if(n == 1){
      return new BigDecimal[]{ BigDecimal.ZERO, BigDecimal.ONE };
    }
    BigDecimal[] f = fibonacci(n - 1);
    return new BigDecimal[]{ f[1], f[0].add(f[1]) };
  }
  public static void main(String[] args){
    long t0 = System.nanoTime();
    BigDecimal[] f = fibonacci(10000);
    System.out.printf("%.3fms%n", (System.nanoTime() - t0) / 1000000.0);
    System.out.printf("%s%n", f[0].add(f[1]));
    return;
  }
}
実行統計の出力

-XX:+CITime オプションを使用するとプログラム終了時に JIT コンパイラの実行統計が出力されます。

torao@safran$ java -XX:+CITime A
28.132ms
5443837311356528133…97501

Accumulated compiler times (for compiled methods only)
------------------------------------------------
  Total compilation time   :  0.046 s
    Standard compilation   :  0.046 s, Average : 0.004
    On stack replacement   :  0.000 s, Average : nan

  Total compiled bytecodes :   2212 bytes
    Standard compilation   :   2212 bytes
    On stack replacement   :      0 bytes
  Average compilation speed:  48146 bytes/s

  nmethod code size        :   9408 bytes
  nmethod total size       :  17256 bytes
コンパイルごとの出力

-XX:+PrintCompilation オプションは JIT コンパイルが行われるたびにメッセージを表示します。以下の実行では fibonacci() メソッドと共に実行回数の多い BigDecimal のメソッドがコンパイルされています。

torao@safran$ java -XX:+PrintCompilation A
     74   1       A::fibonacci (73 bytes)
     81   2       java.math.BigInteger::add (178 bytes)
     93   3       java.lang.Object::<init> (1 bytes)
     95   4       java.lang.Number::<init> (5 bytes)
    103   5       java.math.BigDecimal::<init> (27 bytes)
31.522ms
    125   6       java.math.BigDecimal::add (302 bytes)
    135   7       java.math.MutableBigInteger::mulsub (110 bytes)
    138   8       java.math.MutableBigInteger::primitiveLeftShift (89 bytes)
    142   9       java.math.BigInteger::<init> (24 bytes)
    145  10       java.math.MutableBigInteger::unsignedLongCompare (20 bytes)
    147  11       java.math.MutableBigInteger::divideMagnitude (594 bytes)
    163  12       java.math.MutableBigInteger::normalize (91 bytes)
5443837311356528133…97501
コンパイルしきい値の調整

-XX:CompileThreshold オプションを使用して JIT コンパイルの行われるタイミングを調整することができます。デフォルトの 10000 を基準に、アプリケーション起動からなるべく早めに JIT コンパイルを済ませたい場合は少ない数値を、JIT コンパイルの影響をなるべく分散させたい場合は大きな値を指定します。以下の例では 1 を指定しているのでブートストラップからほとんどのメソッドが JIT コンパイルされています。

torao@safran$ java -XX:CompileThreshold=1 -XX:+CITime -XX:+PrintCompilation A
---   n   java.lang.Thread::currentThread (static)
---   n   java.lang.System::arraycopy (static)
---   n   java.security.AccessController::getStackAccessControlContext (static)
---   n   java.security.AccessController::getInheritedAccessControlContext (static)
---   n   java.lang.Thread::setPriority0
     81   2       java.lang.Object::<init> (1 bytes)
     85   3       java.lang.Thread::init (193 bytes)
…
---   n   java.lang.Double::longBitsToDouble (static)
---   n   java.lang.Float::floatToRawIntBits (static)
---   n   java.io.FileOutputStream::writeBytes
236.170ms
5443837311356528133…97501

Accumulated compiler times (for compiled methods only)
------------------------------------------------
  Total compilation time   :  2.239 s
    Standard compilation   :  2.239 s, Average : 0.003
    On stack replacement   :  0.000 s, Average : nan

  Total compiled bytecodes :  79292 bytes
    Standard compilation   :  79292 bytes
    On stack replacement   :      0 bytes
  Average compilation speed:  35407 bytes/s

  nmethod code size        : 362752 bytes
  nmethod total size       : 688752 bytes