美文网首页
native方法调用流程剖析

native方法调用流程剖析

作者: allanYan | 来源:发表于2022-12-06 20:11 被阅读0次

    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()

    相关文章

      网友评论

          本文标题:native方法调用流程剖析

          本文链接:https://www.haomeiwen.com/subject/rshgfdtx.html