native方法调用流程剖析

2022-12-06  本文已影响0人  allanYan

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方法的解释器入口;

调用阶段

  1. 当某个native方法真的被调用时,一开始它会从解释器进入,这就进到最初提到的TemplateInterpreterGenerator::generate_native_entry所生成的代码;
    它会调用InterpreterRuntime::prepare_native_call来获取native函数真正的入口地址;
  2. 这里会先调用m->has_native_function()看之前是否已经在Method对象里记录下了native函数入口地址;
  3. 如果已有地址的话可能是JNI库在JNI_OnLoad()的时候调用了RegisterNatives()来注册函数地址信息,或者可能这已经不是第一调用该native方法;
  4. 如果没有记录下函数地址,就调用NativeLookup::lookup来寻找native方法真正的目标在什么地方,然后把它记在Method里;
  5. NativeLookup::lookup()里会通过NativeLookup::pure_jni_name()来构造出符合JNI规范的函数名,然后通过os::dll_lookup()在查找路径中能找到的动态链接库里去找这个名字对应的地址;
  6. 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);
}
  1. 当一个native方法被调用足够多次之后,HotSpot会为它生成专门的入口(替换掉原本通用的解释器入口)。这种入口叫做native wrapper。Signature相同的native方法共享同一个native wrapper;

调用链为CompileBroker::compile_method()
-> AdapterHandlerLibrary::create_native_wrapper(method)
-> SharedRuntime::generate_native_wrapper()

上一篇下一篇

猜你喜欢

热点阅读