Up Top

JNI の処理 : JNI Functions の処理 : JNI によるフィールドアクセス : GetField() の高速アクセス処理


該当する JNI 関数

概要(Summary)

高速化版のコードは, JNI_FastGetField クラスのメソッドによって実行時に生成される (See: here for details). プラットフォーム毎およびアクセス対象の型毎に, それぞれ以下のメソッドでコードが生成される.

高速版では様々なチェック処理が省略されている. ただし, フィールドの位置は safepoint 停止時に (オブジェクトごと) 変更されることがあるため, safepoint のチェックだけは必要になる. この safepoint チェックを高速に行うため, 以下のように処理を行っている.

  1. フィールド取得の前後で SafepointSynchronize::_safepoint_counter というカウンタの値を確認する.

  2. もし取得前後で値が同じであれば safepoint が起こらなかったということなので, 取得した値をそのまま使う. 取得前にカウンタ値が奇数(現在 safepoint 実行中)であったり, 取得前後で値に変化があれば, 念のため低速版 (jni_GetXXXField()) にフォールバックする.

    ((cite: hotspot/src/share/vm/prims/jniFastGetField.hpp))
    // Basic logic of a fast version of jni_Get<Primitive>Field:
    //
    // (See safepoint.hpp for a description of _safepoint_counter)
    //
    // load _safepoint_counter into old_counter
    // IF old_counter is odd THEN
    //   a safepoint is going on, return jni_GetXXXField
    // ELSE
    //   load the primitive field value into result (speculatively)
    //   load _safepoint_counter into new_counter
    //   IF (old_counter == new_counter) THEN
    //     no safepoint happened during the field access, return result
    //   ELSE
    //     a safepoint might have happened in-between, return jni_GetXXXField()
    //   ENDIF
    // ENDIF
    //
    // LoadLoad membars to maintain the load order may be necessary
    // for some platforms.
    //
    // The fast versions don't check for pending suspension request.
    // This is fine since it's totally read-only and doesn't create new race.
    //
    // There is a hypothetical safepoint counter wraparound. But it's not
    // a practical concern.

ただし, 実行タイミングが本当に safepoint 処理と重なった場合, 上のチェックを行っても変なアドレスにアクセスしてメモリアクセス違反を起こすことがある. この場合には signal handler でキャッチして正しい処理へとフォールバックさせている.

このフォールバックを実現するため, あらかじめ JNI_FastGetField 内に 「メモリアクセス違反を起こす危険性のあるロード命令のアドレス」と 「その場合のフォールバック処理のエントリポイント」を記録している. signal handler ではそれを見て対応するエントリポイントに遷移させる.

(メモリアクセス違反を起こす危険性のあるロード命令のアドレスとその場合のフォールバック処理のエントリポイントは, JNI_FastGetField::speculative_load_pclist という配列と JNI_FastGetField::slowcase_entry_pclist という配列にそれぞれ納められている)

    ((cite: hotspot/src/share/vm/prims/jniFastGetField.cpp))
    address JNI_FastGetField::speculative_load_pclist [LIST_CAPACITY];
    address JNI_FastGetField::slowcase_entry_pclist   [LIST_CAPACITY];

備考(Notes)

safepoint の開始時と終了時に increment される. (値が変わっていれば safepoint 処理が実行されたということになる. また, 値が奇数であれば現在 safepoint 処理が実行されているということになる)

    ((cite: hotspot/src/share/vm/runtime/safepoint.hpp))
      // This counter is used for fast versions of jni_Get<Primitive>Field.
      // An even value means there is no ongoing safepoint operations.
      // The counter is incremented ONLY at the beginning and end of each
      // safepoint. The fact that Threads_lock is held throughout each pair of
      // increments (at the beginning and end of each safepoint) guarantees
      // race freedom.
    public:
      static volatile int _safepoint_counter;

(<= 今の x86 で load-load の追い抜きってあったっけ?? Power とか ARM ならともかく... #TODO)

    ((cite: hotspot/src/cpu/x86/vm/jniFastGetField_x86_64.cpp))
    // Instead of issuing lfence for LoadLoad barrier, we create data dependency
    // between loads, which is more efficient than lfence.

    // Common register usage:
    // rax/xmm0: result
    // c_rarg0:    jni env
    // c_rarg1:    obj
    // c_rarg2:    jfield id

    static const Register robj          = r9;
    static const Register rcounter      = r10;
    static const Register roffset       = r11;
    static const Register rcounter_addr = r11;

    // Warning: do not use rip relative addressing after the first counter load
    // since that may scratch r10!

処理の流れ (概要)(Execution Flows : Summary)

高速アクセス処理

JNI_FastGetField::generate_fast_get_<type>_field()  が生成するコード
-> JNI_FastGetField::generate_fast_get_{int|long|float}_field0()  が生成するコード
   -> (高速アクセスが失敗した場合は以下の関数にフォールバック)
       jni_Get<type>Field()

シグナルハンドラによる slow case へのフォールバック処理 (メモリアクセス違反時の処理)

(See: here for details)
-> シグナルハンドラ (JVM_handle_linux_signal() or JVM_handle_solaris_signal() or topLevelExceptionFilter())
   -> (1) アクセス違反を起こした pc に対応するフォールバック処理エントリポイントを取得
          -> JNI_FastGetField::find_slowcase_pc()
      (2) ハンドラ終了後、フォールバック位置から再開するように設定

処理の流れ (詳細)(Execution Flows : Details)

JNI_FastGetField::generate_fast_get_boolean_field() (sparc の場合)

See: here for details

JNI_FastGetField::generate_fast_get_byte_field() (sparc の場合)

See: here for details

JNI_FastGetField::generate_fast_get_char_field() (sparc の場合)

See: here for details

JNI_FastGetField::generate_fast_get_short_field() (sparc の場合)

See: here for details

JNI_FastGetField::generate_fast_get_int_field() (sparc の場合)

See: here for details

JNI_FastGetField::generate_fast_get_int_field0() (sparc の場合)

See: here for details

JNI_FastGetField::generate_fast_get_long_field() (sparc の場合)

See: here for details

JNI_FastGetField::generate_fast_get_float_field() (sparc の場合)

See: here for details

JNI_FastGetField::generate_fast_get_double_field() (sparc の場合)

See: here for details

JNI_FastGetField::generate_fast_get_float_field0() (sparc の場合)

See: here for details

JNI_FastGetField::generate_fast_get_boolean_field() (x86_64 の場合)

See: here for details

JNI_FastGetField::generate_fast_get_byte_field() (x86_64 の場合)

See: here for details

JNI_FastGetField::generate_fast_get_char_field() (x86_64 の場合)

See: here for details

JNI_FastGetField::generate_fast_get_short_field() (x86_64 の場合)

See: here for details

JNI_FastGetField::generate_fast_get_int_field() (x86_64 の場合)

See: here for details

JNI_FastGetField::generate_fast_get_long_field() (x86_64 の場合)

See: here for details

JNI_FastGetField::generate_fast_get_int_field0() (x86_64 の場合)

See: here for details

JNI_FastGetField::generate_fast_get_float_field() (x86_64 の場合)

See: here for details

JNI_FastGetField::generate_fast_get_double_field() (x86_64 の場合)

See: here for details

JNI_FastGetField::generate_fast_get_float_field0() (x86_64 の場合)

See: here for details

JNI_FastGetField::find_slowcase_pc()

See: here for details


This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.