Hotspot 编译代码的安装与卸载 源码解析

目录

一、nmethod

1、定义

2、new_nmethod

二、ciEnv

1、定义

2、ciBaseObject

3、构造和析构函数

4、ciEnv::register_method

三、编译代码安装与卸载

1、Method::set_code

2、AdapterHandlerEntry

3、AdapterHandlerLibrary

4、Method::clear_code

5、InstanceKlass::add_osr_nmethod

6、InstanceKlass::remove_osr_nmethod


     上一篇《Hotspot 热点代码编译和栈上替换 源码解析》中我们讨论了热点代码如何判定,热点代码如何完成编译的,如果是循环类的热点代码,如何在编译后完成栈上替换的。热点代码编译完成产生的nmethod都有一个安装的动作,即将其与对应的Method之间完成绑定,让其能够被解释器正常调用,那么安装的动作是哪里完成的了?对栈上替换的nmethod而言,执行栈上替换就相当于安装,因为栈上替换的nmethod都是方法内部的调用,所以实现相对简单点。对非栈上替换的nmethod而言,其安装稍微复杂点,需要考虑从Java代码和本地代码中调用nmethod安装完成的方法的情形,Hotspot的实现是通过一个在字节码解释执行的栈帧和本地代码执行的栈帧之间做切换适配的适配器来完成安装,适配器和字节码指令一样都是通过汇编实现的。

一、nmethod

1、定义

    nmethod继承自CodeBlob,其定义位于hotspot src/share/vm/code/nmethod.hpp中,用于表示一个编译后的Java方法。其包含的内容如下图:

nmethod包含的关键属性如下:

  •  _method:Method*,该nmethod对应的Method
  • _entry_bci:int,如果是栈上替换则不等于InvocationEntryBci
  • _jmethod_id:jmethodID,该nmethod对应的Method的jmethodID
  • _osr_link:nmethod*,取自InstanceKlass::osr_nmethods_head
  • _compiler:AbstractCompiler*,编译此方法的编译器引用
  • _entry_point/_verified_entry_point/_osr_entry_point: address,编译后的本地代码的调用入口
  • _exception_offset/_deoptimize_offset等:各部分的偏移量
  • _compile_id:int,采用了什么类型的编译
  • _comp_level:int,编译级别
  • _state:char,当前nmethod的状态,alive, not_entrant, zombie, unloaded几种
  • _exception_cache,ExceptionCache,原Java方法的异常处理代码缓存

nmethod定义的public方法主要有以下几种:

  • new_nmethod/new_native_nmethod: 创建nmethod
  • 属性相关的,如dec_hotness_counter,entry_point,is_alive,unloading_next等
  • 获取各部分的起止地址,大小,各部分是否包含某个地址等,如consts_begin,consts_end,consts_size,consts_contains等
  • ExceptionCache相关的,如exception_cache,release_set_exception_cache,handler_for_exception_and_pc等
  • GC支持相关的,如oops_do,detect_scavenge_root_oops,preserve_callee_argument_oops等

我们重点关注其初始化方法即可。 

2、new_nmethod

     new_nmethod就是用来创建一个新的nmethod实例,其调用链如下:

 从调用链可知三个编译器Compiler,C2Compiler,SharkCompiler最终都是通过这个方法创建一个新的nmethod实例,然后将编译生成的本地代码写入到nmethod中。其方法实现的源码如下:

nmethod* nmethod::new_nmethod(methodHandle method,
  int compile_id,
  int entry_bci,
  CodeOffsets* offsets,
  int orig_pc_offset,
  DebugInformationRecorder* debug_info,
  Dependencies* dependencies,
  CodeBuffer* code_buffer, int frame_size,
  OopMapSet* oop_maps,
  ExceptionHandlerTable* handler_table,
  ImplicitExceptionTable* nul_chk_table,
  AbstractCompiler* compiler,
  int comp_level
)
{
  assert(debug_info->oop_recorder() == code_buffer->oop_recorder(), "shared OR");
  //找到code_buffer中的所有的oop,为其分配Handler
  code_buffer->finalize_oop_references(method);
  // create nmethod
  nmethod* nm = NULL;
  //获取锁CodeCache_lock
  { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
    //计算nmethod的大小
    int nmethod_size =
      allocation_size(code_buffer, sizeof(nmethod))
      + adjust_pcs_size(debug_info->pcs_size())
      + round_to(dependencies->size_in_bytes() , oopSize)
      + round_to(handler_table->size_in_bytes(), oopSize)
      + round_to(nul_chk_table->size_in_bytes(), oopSize)
      + round_to(debug_info->data_size()       , oopSize);
    
    //从CodeCache中分配内存,分配完成调用下面的nmethod完成初始化
    nm = new (nmethod_size)
    nmethod(method(), nmethod_size, compile_id, entry_bci, offsets,
            orig_pc_offset, debug_info, dependencies, code_buffer, frame_size,
            oop_maps,
            handler_table,
            nul_chk_table,
            compiler,
            comp_level);

    if (nm != NULL) {
      //记录此方法依赖的klass,deoptimization使用
      for (Dependencies::DepStream deps(nm); deps.next(); ) {
        Klass* klass = deps.context_type();
        if (klass == NULL) {
          continue;  // ignore things like evol_method
        }

        // record this nmethod as dependent on this klass
        InstanceKlass::cast(klass)->add_dependent_nmethod(nm);
      }
      NOT_PRODUCT(nmethod_stats.note_nmethod(nm));
      if (PrintAssembly || CompilerOracle::has_option_string(method, "PrintAssembly")) {
        Disassembler::decode(nm);
      }
    }
  }
  // Do verification and logging outside CodeCache_lock.
  if (nm != NULL) {
    // Safepoints in nmethod::verify aren't allowed because nm hasn't been installed yet.
    DEBUG_ONLY(nm->verify();)
    nm->log_new_nmethod();
  }
  return nm;
}

void* nmethod::operator new(size_t size, int nmethod_size) throw() {
  // Not critical, may return null if there is too little continuous memory
  return CodeCache::allocate(nmethod_size);
}

nmethod::nmethod(
  Method* method,
  int nmethod_size,
  int compile_id,
  int entry_bci,
  CodeOffsets* offsets,
  int orig_pc_offset,
  DebugInformationRecorder* debug_info,
  Dependencies* dependencies,
  CodeBuffer *code_buffer,
  int frame_size,
  OopMapSet* oop_maps,
  ExceptionHandlerTable* handler_table,
  ImplicitExceptionTable* nul_chk_table,
  AbstractCompiler* compiler,
  int comp_level
  )
  : CodeBlob("nmethod", code_buffer, sizeof(nmethod),
             nmethod_size, offsets->value(CodeOffsets::Frame_Complete), frame_size, oop_maps),
  _native_receiver_sp_offset(in_ByteSize(-1)),
  _native_basic_lock_sp_offset(in_ByteSize(-1))
{
  assert(debug_info->oop_recorder() == code_buffer->oop_recorder(), "shared OR");
  {
    debug_only(No_Safepoint_Verifier nsv;)
    //确保已经获取锁CodeCache_lock
    assert_locked_or_safepoint(CodeCache_lock);
    //部分属性初始化成默认值
    init_defaults();
    //初始化其他属性
    _method                  = method;
    _entry_bci               = entry_bci;
    _compile_id              = compile_id;
    _comp_level              = comp_level;
    _compiler                = compiler;
    _orig_pc_offset          = orig_pc_offset;
    _hotness_counter         = NMethodSweeper::hotness_counter_reset_val();

    //计算各部分偏移量
    _consts_offset           = content_offset()      + code_buffer->total_offset_of(code_buffer->consts());
    _stub_offset             = content_offset()      + code_buffer->total_offset_of(code_buffer->stubs());

    // Exception handler and deopt handler are in the stub section
    assert(offsets->value(CodeOffsets::Exceptions) != -1, "must be set");
    assert(offsets->value(CodeOffsets::Deopt     ) != -1, "must be set");
    _exception_offset        = _stub_offset          + offsets->value(CodeOffsets::Exceptions);
    _deoptimize_offset       = _stub_offset          + offsets->value(CodeOffsets::Deopt);
    if (offsets->value(CodeOffsets::DeoptMH) != -1) {
      _deoptimize_mh_offset  = _stub_offset          + offsets->value(CodeOffsets::DeoptMH);
    } else {
      _deoptimize_mh_offset  = -1;
    }
    if (offsets->value(CodeOffsets::UnwindHandler) != -1) {
      _unwind_handler_offset = code_offset()         + offsets->value(CodeOffsets::UnwindHandler);
    } else {
      _unwind_handler_offset = -1;
    }

    _oops_offset             = data_offset();
    _metadata_offset         = _oops_offset          + round_to(code_buffer->total_oop_size(), oopSize);
    _scopes_data_offset      = _metadata_offset      + round_to(code_buffer->total_metadata_size(), wordSize);

    _scopes_pcs_offset       = _scopes_data_offset   + round_to(debug_info->data_size       (), oopSize);
    _dependencies_offset     = _scopes_pcs_offset    + adjust_pcs_size(debug_info->pcs_size());
    _handler_table_offset    = _dependencies_offset  + round_to(dependencies->size_in_bytes (), oopSize);
    _nul_chk_table_offset    = _handler_table_offset + round_to(handler_table->size_in_bytes(), oopSize);
    _nmethod_end_offset      = _nul_chk_table_offset + round_to(nul_chk_table->size_in_bytes(), oopSize);

    _entry_point             = code_begin()          + offsets->value(CodeOffsets::Entry);
    _verified_entry_point    = code_begin()          + offsets->value(CodeOffsets::Verified_Entry);
    _osr_entry_point         = code_begin()          + offsets->value(CodeOffsets::OSR_Entry);
    _exception_cache         = NULL;
    _pc_desc_cache.reset_to(scopes_pcs_begin());

    //将code_buffer中的oop_recoder等复制到nmethod中
    code_buffer->copy_values_to(this);
    debug_info->copy_to(this);
    dependencies->copy_to(this);
    if (ScavengeRootsInCode) {
      if (detect_scavenge_root_oops()) {
        CodeCache::add_scavenge_root_nmethod(this);
      }
      //注册该本地方法
      Universe::heap()->register_nmethod(this);
    }
    debug_only(verify_scavenge_root_oops());
    
    //表示分配完成
    CodeCache::commit(this);

    //将ExceptionHandlerTable 的内容复制到nmethod
    handler_table->copy_to(this);
    nul_chk_table->copy_to(this);

    // we use the information of entry points to find out if a method is
    // static or non static
    assert(compiler->is_c2() ||
           _method->is_static() == (entry_point() == _verified_entry_point),
           " entry points must be same for static methods and vice versa");
  }

  bool printnmethods = PrintNMethods
    || CompilerOracle::should_print(_method)
    || CompilerOracle::has_option_string(_method, "PrintNMethods");
  if (printnmethods || PrintDebugInfo || PrintRelocations || PrintDependencies || PrintExceptionHandlers) {
    print_nmethod(printnmethods);
  }
}

void nmethod::init_defaults() {
  _state                      = in_use;
  _unloading_clock            = 0;
  _marked_for_reclamation     = 0;
  _has_flushed_dependencies   = 0;
  _has_unsafe_access          = 0;
  _has_method_handle_invokes  = 0;
  _lazy_critical_native       = 0;
  _has_wide_vectors           = 0;
  _marked_for_deoptimization  = 0;
  _lock_count                 = 0;
  _stack_traversal_mark       = 0;
  _unload_reported            = false;           // jvmti state

#ifdef ASSERT
  _oops_are_stale             = false;
#endif

  _oops_do_mark_link       = NULL;
  _jmethod_id              = NULL;
  _osr_link                = NULL;
  if (UseG1GC) {
    _unloading_next        = NULL;
  } else {
    _scavenge_root_link    = NULL;
  }
  _scavenge_root_state     = 0;
  _compiler                = NULL;
#if INCLUDE_RTM_OPT
  _rtm_state               = NoRTM;
#endif
#ifdef HAVE_DTRACE_H
  _trap_offset             = 0;
#endif // def HAVE_DTRACE_H
}

二、ciEnv

1、定义

   ciEnv继承自StackObj,其定义位于hotspot src/share/vm/ci/ciEnv.hpp,相当于执行编译任务的一个中间类,充当编译器,后台编译线程同JVM运行时环境之间的桥梁。ciEnv定义的重要属性如下:

  • _ciEnv_arena:Arena,用来执行内存分配
  • _arena:Arena*,实际指向_ciEnv_arena
  • _system_dictionary_modification_counter:int,系统字典修改计数器
  • _factory:ciObjectFactory*,用来创建ciObject及其子类实例,会借助_ciEnv_arena来获取内存
  • _oop_recorder:OopRecorder*,用来临时保存被编译方法的oop_recorder
  • _debug_info:DebugInformationRecorder*,用来临时保存被编译方法的debug_info
  • _dependencies:Dependencies*,用来临时保存被编译方法的dependencies
  • _failure_reason:char*,失败原因
  • _compilable:int,实际是一个枚举值表示方法编译情况,其枚举值有三个MethodCompilable,MethodCompilable_not_at_tier,MethodCompilable_never
  • _break_at_compile:bool,是否在编译时终止
  • _num_inlined_bytecodes:int,内联字节码的个数
  • _task:CompileTask*,指向CompilerThread::task
  • _log:CompileLog*,指向CompilerThread::log
  • _compiler_data:void* ,编译的数据

   ciEnv定义的public方法中大部分是读写属性相关的,重点关注用于注册编译结果的register_method方法和ciEnv本身的初始化和析构方法。ciEnv定义了大量的创建ciObject及其子类实例的私有方法,如get_klass_by_name,get_klass_by_index,get_field_by_index_impl等,这些私有方法可以被友元类CompileBroker和Dependencies两个类直接调用。

2、ciBaseObject

     ciBaseObject是ciObject的基类,其类继承关系如下:

除上述类以外,还有直接继承自ResourceObj的ciField,ciMethodBlocks,ciSignature等类,这些类全部位于hotspot src/share/vm/ci目录下,如下图:

这些类的意义在于将后台编译与GC隔离开来,允许这两者可以不需要任何接口交互,独立的互不干扰的进行。其实现也比较简单就是保存一个指向保存特定类oop的handler的指针,并保存该实例的部分关键属性,以ciMethod构造方法为例说明:

//h_m是JVM运行时保存方法指针的Handler,holder是该方法所属的类的klass经过转换后的ciObject实例
ciMethod::ciMethod(methodHandle h_m, ciInstanceKlass* holder) :
  //ciMetadata是ciMethod的父类,这里实际是给_metadata属性赋值,该属性是Metadata*类型,Metadata是Method的基类
  //即通过_metadata属性保存对应的Method
  ciMetadata(h_m()),
  _holder(holder)
{
  assert(h_m() != NULL, "no null method");

  //保存Method中的关键属性,并做适当的转换
  _flags = ciFlags(h_m()->access_flags());

  _max_stack          = h_m()->max_stack();
  _max_locals         = h_m()->max_locals();
  _code_size          = h_m()->code_size();
  _intrinsic_id       = h_m()->intrinsic_id();
  _handler_count      = h_m()->exception_table_length();
  _size_of_parameters = h_m()->size_of_parameters();
  _uses_monitors      = h_m()->access_flags().has_monitor_bytecodes();
  _balanced_monitors  = !_uses_monitors || h_m()->access_flags().is_monitor_matching();
  _is_c1_compilable   = !h_m()->is_not_c1_compilable();
  _is_c2_compilable   = !h_m()->is_not_c2_compilable();
  // Lazy fields, filled in on demand.  Require allocation.
  _code               = NULL;
  _exception_handlers = NULL;
  _liveness           = NULL;
  _method_blocks = NULL;
#if defined(COMPILER2) || defined(SHARK)
  _flow               = NULL;
  _bcea               = NULL;
#endif // COMPILER2 || SHARK

  ciEnv *env = CURRENT_ENV;
  //处理方法编译相关的属性
  if (env->jvmti_can_hotswap_or_post_breakpoint() && can_be_compiled()) {
    // 6328518 check hotswap conditions under the right lock.
    MutexLocker locker(Compile_lock);
    if (Dependencies::check_evol_method(h_m()) != NULL) {
      _is_c1_compilable = false;
      _is_c2_compilable = false;
    }
  } else {
    CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops());
  }

  if (h_m()->method_holder()->is_linked()) {
    _can_be_statically_bound = h_m()->can_be_statically_bound();
  } else {
    // Have to use a conservative value in this case.
    _can_be_statically_bound = false;
  }

  // Adjust the definition of this condition to be more useful:
  // %%% take these conditions into account in vtable generation
  if (!_can_be_statically_bound && h_m()->is_private())
    _can_be_statically_bound = true;
  if (_can_be_statically_bound && h_m()->is_abstract())
    _can_be_statically_bound = false;

   //保存方法的方法名,方法描述符,常量池等关键信息,即使GC过程中移除了该方法,也不影响编译
  _name = env->get_symbol(h_m()->name());
  ciSymbol* sig_symbol = env->get_symbol(h_m()->signature());
  constantPoolHandle cpool = h_m()->constants();
  _signature = new (env->arena()) ciSignature(_holder, cpool, sig_symbol);
  _method_data = NULL;
  // Take a snapshot of these values, so they will be commensurate with the MDO.
  if (ProfileInterpreter || TieredCompilation) {
    int invcnt = h_m()->interpreter_invocation_count();
    // if the value overflowed report it as max int
    _interpreter_invocation_count = invcnt < 0 ? max_jint : invcnt ;
    _interpreter_throwout_count   = h_m()->interpreter_throwout_count();
  } else {
    _interpreter_invocation_count = 0;
    _interpreter_throwout_count = 0;
  }
  if (_interpreter_invocation_count == 0)
    _interpreter_invocation_count = 1;
  _instructions_size = -1;

}

3、构造和析构函数

      ciEnv的构造方法有两个,其中ciEnv(Arena* arena);只在编译器相关设施初始化时调用,这里忽略,重点关注另外一个ciEnv(CompileTask* task, int system_dictionary_modification_counter);的实现,该方法的调用如下:

CompileBroker::compiler_thread_loop()方法就是后台编译线程执行编译的入口方法,从调用链可知每次执行一个CompileTask,就会创建一个新的ciEnv实例。其源码及析构方法源码说明如下:

ciEnv::ciEnv(CompileTask* task, int system_dictionary_modification_counter)
  //初始化_ciEnv_arena
  : _ciEnv_arena(mtCompiler) {
  VM_ENTRY_MARK;

  //将当前ciEnv实例设置为当前线程的激活ciEnv
  thread->set_env(this);
  assert(ciEnv::current() == this, "sanity");

  //属性的初始化

  _oop_recorder = NULL;
  _debug_info = NULL;
  _dependencies = NULL;
  _failure_reason = NULL;
  _compilable = MethodCompilable;
  _break_at_compile = false;
  _compiler_data = NULL;

  _system_dictionary_modification_counter = system_dictionary_modification_counter;
  _num_inlined_bytecodes = 0;
  assert(task == NULL || thread->task() == task, "sanity");
  _task = task;
  _log = NULL;

  // Temporary buffer for creating symbols and such.
  _name_buffer = NULL;
  _name_buffer_len = 0;

  _arena   = &_ciEnv_arena;
  //通过_arena初始化ciObjectFactory
  _factory = new (_arena) ciObjectFactory(_arena, 128);
  
  //校验Universe类是否完成初始化,并从Universe中获取相关异常类的实例指针
  assert(Universe::is_fully_initialized(), "should be complete");

  oop o = Universe::null_ptr_exception_instance();
  assert(o != NULL, "should have been initialized");
  _NullPointerException_instance = get_object(o)->as_instance();
  o = Universe::arithmetic_exception_instance();
  assert(o != NULL, "should have been initialized");
  _ArithmeticException_instance = get_object(o)->as_instance();

  _ArrayIndexOutOfBoundsException_instance = NULL;
  _ArrayStoreException_instance = NULL;
  _ClassCastException_instance = NULL;
  _the_null_string = NULL;
  _the_min_jint_string = NULL;
}

ciEnv::~ciEnv() {
  CompilerThread* current_thread = CompilerThread::current();
  //减少refcount,当ciEnv销毁时,实际保存_factory创建的ciBaseObject实例的_ciEnv_arena会自动销毁
  _factory->remove_symbols();
  //清除当前线程的ciEnv引用
  GUARDED_VM_ENTRY(current_thread->set_env(NULL);)
}

   4、ciEnv::register_method

       ciEnv::register_method用于将编译结果保存到一个nmethod中,如果是非栈上替换方法则将其安装到被编译方法上确保可以立即执行编译后的本地代码,如果是栈上替换方法则将其插入到InstanceKlass保存的osr_nmethods栈上替换方法链表中。其源码说明如下:

void ciEnv::register_method(ciMethod* target,
                            int entry_bci,
                            CodeOffsets* offsets,
                            int orig_pc_offset,
                            CodeBuffer* code_buffer,
                            int frame_words,
                            OopMapSet* oop_map_set,
                            ExceptionHandlerTable* handler_table,
                            ImplicitExceptionTable* inc_table,
                            AbstractCompiler* compiler,
                            int comp_level,
                            bool has_unsafe_access,
                            bool has_wide_vectors,
                            RTMState  rtm_state) {
  VM_ENTRY_MARK;
  nmethod* nm = NULL;
  {
    //获取锁MethodCompileQueue_lock,注意此时的线程都是CompilerThread::current()
    MutexLocker locker(MethodCompileQueue_lock, THREAD);

    //获取锁Compile_lock,避免并发执行SystemDictionary::add_to_hierarchy,直到目标方法的编译结果已经安装完成
    //也不允许任何safepoints,否则有可能发生类的重定义
    MutexLocker ml(Compile_lock);
    No_Safepoint_Verifier nsv;

    //校验JVMTI的状态
    if (!failing() &&
        ( (!jvmti_can_hotswap_or_post_breakpoint() &&
           JvmtiExport::can_hotswap_or_post_breakpoint()) ||
          (!jvmti_can_access_local_variables() &&
           JvmtiExport::can_access_local_variables()) ||
          (!jvmti_can_post_on_exceptions() &&
           JvmtiExport::can_post_on_exceptions()) )) {
      record_failure("Jvmti state change invalidated dependencies");
    }

    //校验Dtrace的相关标签
    if (!failing() &&
        ( (!dtrace_extended_probes() && ExtendedDTraceProbes) ||
          (!dtrace_method_probes() && DTraceMethodProbes) ||
          (!dtrace_alloc_probes() && DTraceAllocProbes) )) {
      record_failure("DTrace flags change invalidated dependencies");
    }

    if (!failing()) {
      if (log() != NULL) {
        //打印被编译方法的所有依赖项
        dependencies()->log_all_dependencies();
      }

      //将这些依赖项进行编码,保存到Dependencies的_content_bytes属性
      dependencies()->encode_content_bytes();

      // Check for {class loads, evolution, breakpoints, ...} during compilation
      //校验被编译方法的依赖项,调用地址等在方法编译期间是否改变导致编译无效,如果校验失败通过record_failure方法记录失败原因
      validate_compile_task_dependencies(target);
    }

    methodHandle method(THREAD, target->get_Method());

    //如果编译失败
    if (failing()) {
      // While not a true deoptimization, it is a preemptive decompile.
      MethodData* mdo = method()->method_data();
      if (mdo != NULL) {
        //增加编译失败计数
        mdo->inc_decompile_count();
      }

      //释放保存编译结果的code_buffer
      code_buffer->free_blob();
      return;
    }
    
    //校验编译结果中必须有deopt和exception handler
    assert(offsets->value(CodeOffsets::Deopt) != -1, "must have deopt entry");
    assert(offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry");
    
    //将编译结果写入一个新的nmethod
    nm =  nmethod::new_nmethod(method,
                               compile_id(),
                               entry_bci,
                               offsets,
                               orig_pc_offset,
                               debug_info(), dependencies(), code_buffer,
                               frame_words, oop_map_set,
                               handler_table, inc_table,
                               compiler, comp_level);
    //因为创建nmethod时会将code_buffer中的内容复制到nmethod中,所以这里就释放code_buffer
    code_buffer->free_blob();

   //如果nmethod创建成功
    if (nm != NULL) {
      //设置nmethod属性
      nm->set_has_unsafe_access(has_unsafe_access);
      nm->set_has_wide_vectors(has_wide_vectors);

      // 给CompileTask打标编译完成
      if (task() != NULL) {
        task()->set_code(nm);
      }
      
      //如果是非栈上替换
      if (entry_bci == InvocationEntryBci) {
        //如果是分层编译
        if (TieredCompilation) {
          //如果有一个低层次优化的nmethod
          nmethod* old = method->code();
          if (TraceMethodReplacement && old != NULL) {
            ResourceMark rm;
            //获取方法名
            char *method_name = method->name_and_sig_as_C_string();
            //打印替换日志
            tty->print_cr("Replacing method %s", method_name);
          }
          if (old != NULL) {
            //改变老的nmethod的状态,将其标记为旧版本
            old->make_not_entrant();
          }
        }
        if (TraceNMethodInstalls) {
          ResourceMark rm;
          char *method_name = method->name_and_sig_as_C_string();
          ttyLocker ttyl;
          //打印nmethod安装日志
          tty->print_cr("Installing method (%d) %s ",
                        comp_level,
                        method_name);
        }
        //nmethod安装到Method上,安装完成可以立即执行
        method->set_code(method, nm);
      } else {//如果是栈上替换
        if (TraceNMethodInstalls) {
          ResourceMark rm;
          char *method_name = method->name_and_sig_as_C_string();
          ttyLocker ttyl;
          tty->print_cr("Installing osr method (%d) %s @ %d",
                        comp_level,
                        method_name,
                        entry_bci);
        }
        //将其添加到Klass的osr_nmethods链表上
        method->method_holder()->add_osr_nmethod(nm);
      }
    }
  }  // safepoints are allowed again

  if (nm != NULL) {
    //发布方法编译完成事件
    nm->post_compiled_method_load_event();
  } else {
    //编译失败,处理CodeCache已满
    record_failure("code cache is full");
    CompileBroker::handle_full_code_cache();
  }
}

其中VM_ENTRY_MARK宏的定义如下:

即上述方法中的THRAD不再是通常意义的JavaThread而是后台编译线程CompilerThread。

该方法的调用链如下:

即编译器编译完成后会调用该方法完成编译代码的安装。 

三、编译代码安装与卸载

1、Method::set_code

     Method::set_code方法就是执行编译代码安装到对应的Method实例的方法,该方法的源码说明如下:

void Method::set_code(methodHandle mh, nmethod *code) {
  //获取锁Patching_lock
  MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag);
  //校验code非空
  assert( code, "use clear_code to remove code" );
  //校验method原来的code为空或者不是栈上替换的nmethod
  assert( mh->check_code(), "" );
  
  //校验adaper不为空
  guarantee(mh->adapter() != NULL, "Adapter blob must already exist!");

  //下面的属性修改必须按照顺序依次执行
  //将目标方法的_code属性置为code
  mh->_code = code;             // Assign before allowing compiled code to exec
  //获取编译级别
  int comp_level = code->comp_level();
  //如果大于该方法曾经的最高编译级别则更新
  if (comp_level > mh->highest_comp_level()) {
    mh->set_highest_comp_level(comp_level);
  }
 
 //让上述修改立即生效,内存屏障相关
  OrderAccess::storestore();
#ifdef SHARK
  //如果采用Shark编译器,则直接将方法调用入口地址改写成insts_begin
  mh->_from_interpreted_entry = code->insts_begin();
#else //如果采用非Shark即使用C1或者C2编译器
  //将_from_compiled_entry赋值成verified_entry_point
  mh->_from_compiled_entry = code->verified_entry_point();
  OrderAccess::storestore();
  //如果不是MethodHandle的invoke等方法,即编译代码可以立即执行
  if (!mh->is_method_handle_intrinsic())
    //get_i2c_entry方法实际返回的是一个适配器,在_from_interpreted_entry和_from_compiled_entry之间的适配器
    mh->_from_interpreted_entry = mh->get_i2c_entry();
#endif //!SHARK
}

address Method::get_i2c_entry() {
  assert(_adapter != NULL, "must have");
  return _adapter->get_i2c_entry();
}

2、AdapterHandlerEntry

     Method的_adapter属性就是一个AdapterHandlerEntry指针,该类表示一个栈帧转换的适配器,因为解释器做字节码解释执行时的栈帧结构和寄存器的使用与编译后的本地代码执行时的栈帧结构不同,需要在字节码解释执行和本地代码执行两者之间切换时对栈帧和寄存器等做必要的转换处理。其切换有两个方向,从字节码解释执行切换到本地代码执行(即I2C)以及从本地代码执行切换到字节码解释执行(即C2I)。AdapterHandlerEntry本身很简单,只是一个保存I2C和C2I适配器地址的容器而已,其定义的属性如下:

相关属性就是通过init方法完成初始化的,如下:

 Method的_adapter 属性是通过Method::make_adapters完成初始化的,该方法的调用链如下:

即在执行方法链接时就会调用该方法,该方法的源码如下:

3、AdapterHandlerLibrary

     AdapterHandlerLibrary跟AdapterHandlerEntry都定义在同一个文件hotspot src/share/vm/runtime/sharedRuntime.hpp中,是一个用来生成AdapterHandlerEntry的一个工具类,其定义的属性也很简单,如下:

其中buffer用来保存转换的代码,_adapters是一个保存 AdapterHandlerEntry和AdapterFingerPrints之间对应关系的map,_abstract_method_handler是抽象方法对应的AdapterHandlerEntry。

重点关注其get_adapter方法的实现,源码说明如下:

AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(methodHandle method) {
  
  //在获取锁AdapterHandlerLibrary_lock前获取ic_miss的地址,强制其更早初始化,从而避免死锁
  address ic_miss = SharedRuntime::get_ic_miss_stub();
  assert(ic_miss != NULL, "must have handler");

  ResourceMark rm;

  NOT_PRODUCT(int insts_size);
  AdapterBlob* new_adapter = NULL;
  AdapterHandlerEntry* entry = NULL;
  AdapterFingerPrint* fingerprint = NULL;
  {
    //获取锁AdapterHandlerLibrary_lock
    MutexLocker mu(AdapterHandlerLibrary_lock);
    //确保相关属性完成初始化
    initialize();
    //如果是抽象方法
    if (method->is_abstract()) {
      return _abstract_method_handler;
    }

    //获取方法参数个数
    int total_args_passed = method->size_of_parameters(); // All args on stack

    //分配数组
    BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, total_args_passed);
    VMRegPair* regs   = NEW_RESOURCE_ARRAY(VMRegPair, total_args_passed);
    int i = 0;
    //解析方法的参数类型
    if (!method->is_static())  // Pass in receiver first
      sig_bt[i++] = T_OBJECT;
    for (SignatureStream ss(method->signature()); !ss.at_return_type(); ss.next()) {
      sig_bt[i++] = ss.type();  // Collect remaining bits of signature
      if (ss.type() == T_LONG || ss.type() == T_DOUBLE)
        sig_bt[i++] = T_VOID;   // Longs & doubles take 2 Java slots
    }
    assert(i == total_args_passed, "");

    //根据方法的个数和参数类型生成一个AdapterFingerPrint,查找有没有对应的AdapterHandlerEntry
    entry = _adapters->lookup(total_args_passed, sig_bt);

    //如果已存在,则返回
    if (entry != NULL) {
      return entry;
    }

    //计算通过栈帧传递参数的个数,因为用于传递方法参数的寄存器个数和位数有限,多余的参数通过栈帧传递
    int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, false);

    //创建一个新的AdapterFingerPrint
    fingerprint = new AdapterFingerPrint(total_args_passed, sig_bt);

    // StubRoutines::code2() is initialized after this function can be called. As a result,
    // VerifyAdapterCalls and VerifyAdapterSharing can fail if we re-use code that generated
    // prior to StubRoutines::code2() being set. Checks refer to checks generated in an I2C
    // stub that ensure that an I2C stub is called from an interpreter frame.
    bool contains_all_checks = StubRoutines::code2() != NULL;

    //获取_buffer属性,如果为空则创建一个
    BufferBlob* buf = buffer_blob(); // the temporary code buffer in CodeCache
    if (buf != NULL) {
      CodeBuffer buffer(buf);
      short buffer_locs[20];
      //初始化insts区
      buffer.insts()->initialize_shared_locs((relocInfo*)buffer_locs,
                                             sizeof(buffer_locs)/sizeof(relocInfo));

      MacroAssembler _masm(&buffer);
      //通过汇编生成器MacroAssembler生成对应的转换代码,跟CPU架构强相关
      //汇编代码生成后会通过AdapterHandlerLibrary::new_entry创建一个新的AdapterHandlerEntry实例
      entry = SharedRuntime::generate_i2c2i_adapters(&_masm,
                                                     total_args_passed,
                                                     comp_args_on_stack,
                                                     sig_bt,
                                                     regs,
                                                     fingerprint);
     //创建一个新的AdapterBlob来保存生成的汇编代码
      new_adapter = AdapterBlob::create(&buffer);
      NOT_PRODUCT(insts_size = buffer.insts_size());
    }
    //如果new_adapter为空,则说明CodeCache没有剩余空间,返回NULL
    if (new_adapter == NULL) {
      MutexUnlocker mu(AdapterHandlerLibrary_lock);
      CompileBroker::handle_full_code_cache();
      return NULL; 
    }
    //如果new_adapter不为空,将entry保存的转换代码地址指向new_adapter
    entry->relocate(new_adapter->content_begin());

    if (contains_all_checks || !VerifyAdapterCalls) {
      //将新创建的entry保存起来
      _adapters->add(entry);
    }
  }
  // Outside of the lock
  if (new_adapter != NULL) {
    char blob_id[256];
    jio_snprintf(blob_id,
                 sizeof(blob_id),
                 "%s(%s)@" PTR_FORMAT,
                 new_adapter->name(),
                 fingerprint->as_string(),
                 new_adapter->content_begin());
    //注册该stub
    Forte::register_stub(blob_id, new_adapter->content_begin(),new_adapter->content_end());

    if (JvmtiExport::should_post_dynamic_code_generated()) {
      //发布事件
      JvmtiExport::post_dynamic_code_generated(blob_id, new_adapter->content_begin(), new_adapter->content_end());
    }
  }
  return entry;
}

void AdapterHandlerLibrary::initialize() {
  if (_adapters != NULL) return;
  _adapters = new AdapterHandlerTable();

  //抽象方法无法执行,所以如果通过adaper执行抽象方法,会直接抛出异常
  address wrong_method_abstract = SharedRuntime::get_handle_wrong_method_abstract_stub();
  _abstract_method_handler = AdapterHandlerLibrary::new_entry(new AdapterFingerPrint(0, NULL),
                                                              StubRoutines::throw_AbstractMethodError_entry(),
                                                              wrong_method_abstract, wrong_method_abstract);
}

AdapterHandlerEntry* lookup(int total_args_passed, BasicType* sig_bt) {
    NOT_PRODUCT(_lookups++);
    AdapterFingerPrint fp(total_args_passed, sig_bt);
    //计算hash值
    unsigned int hash = fp.compute_hash();
    int index = hash_to_index(hash);
    //查找hash值相等的
    for (AdapterHandlerEntry* e = bucket(index); e != NULL; e = e->next()) {
      NOT_PRODUCT(_buckets++);
      if (e->hash() == hash) {
        NOT_PRODUCT(_equals++);
        if (fp.equals(e->fingerprint())) {
          return e;
        }
      }
    }
    return NULL;
  }

BufferBlob* AdapterHandlerLibrary::buffer_blob() {
  // Should be called only when AdapterHandlerLibrary_lock is active.
  if (_buffer == NULL) // Initialize lazily
      _buffer = BufferBlob::create("adapters", AdapterHandlerLibrary_size);
  return _buffer;
}

void AdapterHandlerEntry::relocate(address new_base) {
  address old_base = base_address();
  assert(old_base != NULL, "");
  ptrdiff_t delta = new_base - old_base;
  //重置_i2c_entry等调用地址
  if (_i2c_entry != NULL)
    _i2c_entry += delta;
  if (_c2i_entry != NULL)
    _c2i_entry += delta;
  if (_c2i_unverified_entry != NULL)
    _c2i_unverified_entry += delta;
  assert(base_address() == new_base, "");
}


#define NEW_RESOURCE_ARRAY(type, size)\
  (type*) resource_allocate_bytes((size) * sizeof(type))

4、Method::clear_code

     Method::clear_code方法就是编译代码即nmethod卸载的实现方法,该方法的调用链如下:

从上述调用链可知当nmethod本身的状态变更或者CodeCache垃圾回收都会调用该方法。其源码说明如下:

void Method::clear_code(bool acquire_lock /* = true */) {
  //获取锁
  MutexLockerEx pl(acquire_lock ? Patching_lock : NULL, Mutex::_no_safepoint_check_flag);
  // 只有给Method分配内存时_adapter可能为空,初始化完成不可能为空
  if (_adapter == NULL) {
    _from_compiled_entry    = NULL;
  } else {
    //将_from_compiled_entry恢复成初始的c2i_entry
    _from_compiled_entry    = _adapter->get_c2i_entry();
  }
  //让上述修改立即生效
  OrderAccess::storestore();
  //将_from_interpreted_entry恢复成_i2i_entry,_i2i_entry最初赋值和_from_interpreted_entry一样,即正常的字节码解释执行入口
  //当执行 Method::set_code后,_from_interpreted_entry变成了i2c_entry
  _from_interpreted_entry = _i2i_entry;
  OrderAccess::storestore();
  _code = NULL;
}

这里总结下Method的三个属性_i2i_entry,_from_compiled_entry和_from_interpreted_entry的具体含义,初始状态下_i2i_entry和_from_interpreted_entry在值是一样的,即正常的字节码解释执行的入口地址,_from_compiled_entry的值是adaper的c2i_entry,从本地代码中调用Java方法的入口地址,即从本地代码执行跳转到字节码解释执行。当执行Method::set_code方法后,原来的Java方法变成了本地方法,这时_from_interpreted_entry变成了adaper的i2c_entry,即其他Java方法调用该方法的入口地址从正常的_i2i_entry变成了adaper的i2c_entry,因为此时是从字节码执行切换到本地代码执行;_from_compiled_entry变成了nmethod中编译代码的起始地址,即其他本地方法调用该方法从c2i_entry变成了直接的起始地址,因为从本地代码中调用该方法的本地代码,不涉及栈帧状态转换。

参考返回_from_compiled_entry地址的from_compiled_entry方法的调用链,如下图:

 其中fixup_callers_callsite方法用于调整方法的调用地址入口。

5、InstanceKlass::add_osr_nmethod

      add_osr_nmethod用于将需要执行栈上替换的nmethod实例插入到InstanceKlass的osr_nmethods链表上,该方法的调用方只有一个,如下图:

 add_osr_nmethod方法的源码说明如下:

void InstanceKlass::add_osr_nmethod(nmethod* n) {
  // only one compilation can be active
  NEEDS_CLEANUP
  //获取锁
  OsrList_lock->lock_without_safepoint_check();
  //校验必须是栈上替换方法
  assert(n->is_osr_method(), "wrong kind of nmethod");
  //将_osr_nmethods_head设置成n的下一个方法
  n->set_osr_link(osr_nmethods_head());
  //将n设置为_osr_nmethods_head
  set_osr_nmethods_head(n);
  // 如果使用分层编译
  if (TieredCompilation) {
    Method* m = n->method();
    //更新最高编译级别
    m->set_highest_osr_comp_level(MAX2(m->highest_osr_comp_level(), n->comp_level()));
  }
  //解锁
  OsrList_lock->unlock();

  // Get rid of the osr methods for the same bci that have lower levels.
  if (TieredCompilation) {
  //查找所有低于nmethod的编译级别的属于同一方法的nmethod实例,将其从osr_nmethods链表上移除
    for (int l = CompLevel_limited_profile; l < n->comp_level(); l++) {
      nmethod *inv = lookup_osr_nmethod(n->method(), n->osr_entry_bci(), l, true);
      if (inv != NULL && inv->is_in_use()) {
        inv->make_not_entrant();
      }
    }
  }
}

nmethod* osr_nmethods_head() const         { return _osr_nmethods_head; };

6、InstanceKlass::remove_osr_nmethod

     与add_osr_nmethod相对应的就是remove_osr_nmethod,用于从osr_nmethod链表上移除nmethod,该方法的调用链如下:

当nmethod被标记成not_entrant或者zombie时,或者执行CodeCache垃圾回收时会调用该方法,其源码说明如下:

void InstanceKlass::remove_osr_nmethod(nmethod* n) {
  // 获取锁
  OsrList_lock->lock_without_safepoint_check();
  // 校验是否栈上替换方法
  assert(n->is_osr_method(), "wrong kind of nmethod");
  nmethod* last = NULL;
  // 获取osr_nmethods链表的头元素
  nmethod* cur  = osr_nmethods_head();
  int max_level = CompLevel_none;  // Find the max comp level excluding n
  Method* m = n->method();
  // 遍历osr_nmethods链表直到遇到n,找到n所属的方法的所有nmehtod的最高编译级别
  while(cur != NULL && cur != n) {
    if (TieredCompilation && m == cur->method()) {
      // Find max level before n
      max_level = MAX2(max_level, cur->comp_level());
    }
    last = cur;
    cur = cur->osr_link();
  }
  nmethod* next = NULL;
  //如果从链表中找到了目标nmethod
  if (cur == n) {
    //将目标nmethod从链表中移除
    next = cur->osr_link();
    if (last == NULL) {
      // Remove first element
      set_osr_nmethods_head(next);
    } else {
      last->set_osr_link(next);
    }
  }
  n->set_osr_link(NULL);
  if (TieredCompilation) {
    cur = next;
    //遍历链表,更新最大编译级别
    while (cur != NULL) {
      // Find max level after n
      if (m == cur->method()) {
        max_level = MAX2(max_level, cur->comp_level());
      }
      cur = cur->osr_link();
    }
    m->set_highest_osr_comp_level(max_level);
  }
  // Remember to unlock again
  OsrList_lock->unlock();
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值