JVM启动阶段
JVM启动时,会为native方法生成解释器入口,调用链为Threads::create_vm->init_globals->interpreter_init_code-> Interpreter::initialize_code()->TemplateInterpreterGenerator::TemplateInterpreterGenerator->TemplateInterpreterGenerator::generate_all()->TemplateInterpreterGenerator.generate_method_entry->TemplateInterpreterGenerator::generate_native_entry->InterpreterRuntime::prepare_native_call->NativeLookup::lookup->NativeLookup::lookup_base->NativeLookup::lookup_entry->NativeLookup::pure_jni_name
// all non-native method kinds
method_entry(zerolocals)
method_entry(zerolocals_synchronized)
method_entry(empty)
method_entry(getter)
method_entry(setter)
method_entry(abstract)
method_entry(java_lang_math_sin )
method_entry(java_lang_math_cos )
method_entry(java_lang_math_tan )
method_entry(java_lang_math_abs )
method_entry(java_lang_math_sqrt )
method_entry(java_lang_math_log )
method_entry(java_lang_math_log10)
method_entry(java_lang_math_exp )
method_entry(java_lang_math_pow )
method_entry(java_lang_math_fmaF )
method_entry(java_lang_math_fmaD )
method_entry(java_lang_ref_reference_get)
// all native method kinds (must be one contiguous block)
Interpreter::_native_entry_begin = Interpreter::code()->code_end();
method_entry(native)
method_entry(native_synchronized)
Interpreter::_native_entry_end = Interpreter::code()->code_end();
method_entry(java_util_zip_CRC32_update)
method_entry(java_util_zip_CRC32_updateBytes)
method_entry(java_util_zip_CRC32_updateByteBuffer)
method_entry(java_util_zip_CRC32C_updateBytes)
method_entry(java_util_zip_CRC32C_updateDirectByteBuffer)
method_entry(java_lang_Float_intBitsToFloat);
method_entry(java_lang_Float_floatToRawIntBits);
method_entry(java_lang_Double_longBitsToDouble);
method_entry(java_lang_Double_doubleToRawLongBits);
char* NativeLookup::pure_jni_name(const methodHandle& method) {
stringStream st;
// Prefix
st.print("Java_");
// Klass name
if (!map_escaped_name_on(&st, method->klass_name())) {
return NULL;
}
st.print("_");
// Method name
if (!map_escaped_name_on(&st, method->name())) {
return NULL;
}
return st.as_string();
}
举例说明:
java.lang.Object.getClass最终会调用Java_java_lang_Object_getClass(JNIEnv *env, jobject this) 方法;
link阶段
通常在该方法第一次被调用的时候触发,invoke字节码指令执行时要先检查调用目标是否已经resolve好了,没有的话就要做resolution。这个路径会把某条invoke字节码指令的参数的符号链接解析(resolve)为实际的method指针然后存在constant pool cache里。这样,接下来解释器就可以通过解析好的method指针找到from_interpreted_entry()进入native方法的解释器入口;
调用阶段
- 当某个native方法真的被调用时,一开始它会从解释器进入,这就进到最初提到的TemplateInterpreterGenerator::generate_native_entry所生成的代码;
它会调用InterpreterRuntime::prepare_native_call来获取native函数真正的入口地址; - 这里会先调用m->has_native_function()看之前是否已经在Method对象里记录下了native函数入口地址;
- 如果已有地址的话可能是JNI库在JNI_OnLoad()的时候调用了RegisterNatives()来注册函数地址信息,或者可能这已经不是第一调用该native方法;
- 如果没有记录下函数地址,就调用NativeLookup::lookup来寻找native方法真正的目标在什么地方,然后把它记在Method里;
- NativeLookup::lookup()里会通过NativeLookup::pure_jni_name()来构造出符合JNI规范的函数名,然后通过os::dll_lookup()在查找路径中能找到的动态链接库里去找这个名字对应的地址;
- Method里有方法调用次数的计数器,而native entry里有递增这个计数器的逻辑;
if (inc_counter) {
generate_counter_incr(&invocation_counter_overflow);
}
void TemplateInterpreterGenerator::generate_counter_incr(Label* overflow) {
Label done;
// Note: In tiered we increment either counters in Method* or in MDO depending if we're profiling or not.
int increment = InvocationCounter::count_increment;
Label no_mdo;
__ bind(no_mdo);
// Increment counter in MethodCounters
const Address invocation_counter(rax,
MethodCounters::invocation_counter_offset() +
InvocationCounter::counter_offset());
__ get_method_counters(rbx, rax, done);
const Address mask(rax, in_bytes(MethodCounters::invoke_mask_offset()));
__ increment_mask_and_jump(invocation_counter, increment, mask, rcx,
false, Assembler::zero, overflow);
__ bind(done);
}
- 当一个native方法被调用足够多次之后,HotSpot会为它生成专门的入口(替换掉原本通用的解释器入口)。这种入口叫做native wrapper。Signature相同的native方法共享同一个native wrapper;
调用链为CompileBroker::compile_method()
-> AdapterHandlerLibrary::create_native_wrapper(method)
-> SharedRuntime::generate_native_wrapper()
网友评论