一部の JVMTI イベントに関しては, イベント通知を有効にすると HotSpot の実行が interpreter だけに制限される ("interp_only_mode"). interp_only_mode の間は HotSpot の挙動が以下のように変わる.
なお, 有効にすると interp_only_mode になるイベント種別は以下の通り (基本的には半端なく重いイベント通知の場合にのみ interp_only_mode になる).
((cite: hotspot/src/share/vm/prims/jvmtiEventController.cpp))
static const jlong INTERP_EVENT_BITS = SINGLE_STEP_BIT | METHOD_ENTRY_BIT | METHOD_EXIT_BIT |
FRAME_POP_BIT | FIELD_ACCESS_BIT | FIELD_MODIFICATION_BIT;
((cite: hotspot/src/share/vm/runtime/thread.hpp))
// Used by the interpreter in fullspeed mode for frame pop, method
// entry, method exit and single stepping support. This field is
// only set to non-zero by the VM_EnterInterpOnlyMode VM operation.
// It can be set to zero asynchronously (i.e., without a VM operation
// or a lock) so we have to be very careful.
int _interp_only_mode;
(コメントに書かれている通り, 有効にする際には VM operation (VM_EnterInterpOnlyMode::doit()) で切り替えているが, 無効にする際には単にフィールドを書き換えるだけでロックも取らない)
(なお, 当然ながら有効にする際に deopt も行うので, 当該スレッドでは一切 compiled code は実行されなくなる)
((cite: hotspot/src/share/vm/prims/jvmtiManageCapabilities.cpp))
// Disable these when tracking the bytecodes
UseFastEmptyMethods = false;
UseFastAccessorMethods = false;
そして, この値によって C++ interpreter では run() / runWithChecks() の切り替えが行われ, template interpreter では生成コードに JVMTI 関係のチェックを挿入するかどうかの切り替えが行われる模様.
(なお, 一見すると JvmtiExport::can_post_interpreter_events の値が 1 になる処理パスがないように見えるが, onload は一度取得されると always に移されるので実際にはあり得る)
(See: here for details)
interp_only_mode の有無により挙動が変わる箇所は以下の通り.
(まず methodOopDesc::from_interpreted_offset() からロードするが, interp_only_mode であれば methodOopDesc::interpreter_entry_offset() の値をロードしなおす)
((cite: hotspot/src/share/vm/runtime/javaCalls.cpp))
// Since the call stub sets up like the interpreter we call the from_interpreted_entry
// so we can go compiled via a i2c. Otherwise initial entry method will always
// run interpreted.
address entry_point = method->from_interpreted_entry();
if (JvmtiExport::can_post_interpreter_events() && thread->is_interp_only_mode()) {
entry_point = method->interpreter_entry();
}
(まず methodOopDesc::from_interpreted_offset() からロードするが, interp_only_mode であれば methodOopDesc::interpreter_entry_offset() の値をロードしなおす)
((cite: hotspot/src/cpu/sparc/vm/interp_masm_sparc.cpp))
// Assume we want to go compiled if available
ld_ptr(G5_method, in_bytes(methodOopDesc::from_interpreted_offset()), target);
if (JvmtiExport::can_post_interpreter_events()) {
// JVMTI events, such as single-stepping, are implemented partly by avoiding running
// compiled code in threads for which the event is enabled. Check here for
// interp_only_mode if these events CAN be enabled.
verify_thread();
Label skip_compiled_code;
const Address interp_only(G2_thread, JavaThread::interp_only_mode_offset());
ld(interp_only, scratch);
tst(scratch);
br(Assembler::notZero, true, Assembler::pn, skip_compiled_code);
delayed()->ld_ptr(G5_method, in_bytes(methodOopDesc::interpreter_entry_offset()), target);
bind(skip_compiled_code);
}
(interp_only_mode であれば methodOopDesc::interpreter_entry_offset() にジャンプする. そうでなければ, methodOopDesc::from_interpreted_offset() にジャンプする)
((cite: hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp))
prepare_to_jump_from_interpreted();
if (JvmtiExport::can_post_interpreter_events()) {
Label run_compiled_code;
// JVMTI events, such as single-stepping, are implemented partly by avoiding running
// compiled code in threads for which the event is enabled. Check here for
// interp_only_mode if these events CAN be enabled.
// interp_only is an int, on little endian it is sufficient to test the byte only
// Is a cmpl faster?
cmpb(Address(r15_thread, JavaThread::interp_only_mode_offset()), 0);
jcc(Assembler::zero, run_compiled_code);
jmp(Address(method, methodOopDesc::interpreter_entry_offset()));
bind(run_compiled_code);
}
jmp(Address(method, methodOopDesc::from_interpreted_offset()));
((cite: hotspot/src/share/vm/runtime/compilationPolicy.cpp))
if (JvmtiExport::can_post_interpreter_events()) {
assert(THREAD->is_Java_thread(), "Wrong type of thread");
if (((JavaThread*)THREAD)->is_interp_only_mode()) {
// If certain JVMTI events (e.g. frame pop event) are requested then the
// thread is forced to remain in interpreted code. This is
// implemented partly by a check in the run_compiled_code
// section of the interpreter whether we should skip running
// compiled code, and partly by skipping OSR compiles for
// interpreted-only threads.
if (bci != InvocationEntryBci) {
reset_counter_for_back_branch_event(method);
return NULL;
}
}
}
((cite: hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp))
if (comp_level == CompLevel_none &&
JvmtiExport::can_post_interpreter_events()) {
assert(THREAD->is_Java_thread(), "Should be java thread");
if (((JavaThread*)THREAD)->is_interp_only_mode()) {
return NULL;
}
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.