• 第61篇安装与卸载编译方法和OSR


    如果方法或OSR完成编译后,还需要安装到对应的位置才能执行。 

    可以看一下Compilation::compile_method()函数,最重要的实现代码如下:

    // compile method
    int frame_size = compile_java_method();
    // ...
    
    if (InstallMethods) { // InstallMethods选项默认的值为true
      install_code(frame_size);
    }
    

    调用compile_java_method()函数编译,这个函数是C1编译器或C2编译器的入口方法,后面在介绍C1编译器时会从这个函数的实现开始介绍。编译完成后调用install_code()函数“安装”代码。install_code()函数的实现如下:

    源代码位置:openjdk/hotspot/src/share/vm/c1/c1_Compilation.cpp
    
    void Compilation::install_code(int frame_size) {
      _env->register_method(
        method(),
        osr_bci(),
        &_offsets,
        in_bytes(_frame_map->sp_offset_for_orig_pc()),
        code(),
        in_bytes(frame_map()->framesize_in_bytes()) / sizeof(intptr_t),
        debug_info_recorder()->_oopmaps,
        exception_handler_table(),
        implicit_exception_table(),
        compiler(),
        _env->comp_level(),
        has_unsafe_access(),
        SharedRuntime::is_wide_vector(max_vector_size())
      );
    }
    

    其中_env变量在Compilation类中的定义如下:

    ciEnv*             _env;

    也就是调用ciEnv中的register_method()函数注册编译的方法(热点代码块也是以方法为单位编译的)。ciEnv相当于执行编译任务的一个中间类,充当编译器、后台编译线程和HotSpot VM运行时环境之间的桥梁。

    调用的register_method()函数的实现如下:

    源代码位置:openjdk/hotspot/src/share/vm/ci/ciEnv.cpp
    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
    ) {
    
      nmethod* nm = NULL;
      {
        // ...    
        methodHandle method(THREAD, target->get_Method());
        // ...
        // 创建一个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中,所以这里需要释放CodeBuffer
        code_buffer->free_blob();
    
        if (nm != NULL) {
          if (task() != NULL) {
            task()->set_code(nm);
          }
    
          //如果是非栈上替换 
          if (entry_bci == InvocationEntryBci) {
            if (TieredCompilation) {
              // 如果有之前安装的版本,设置为不可用
              nmethod* old = method->code();
              if (old != NULL) {
                old->make_not_entrant();
              }
            }
            // nmethod安装到Method上,安装完成可以立即执行
            method->set_code(method, nm);
          } else { // 如果是栈上替换
            // 将其添加到Klass的osr_nmethods链表上
            method->method_holder()->add_osr_nmethod(nm);
          }
        } 
        
      }  
    }    

    可以看到,会将编译完成后的方法设置到Method::_code属性上,调用add_osr_nmethod()函数将栈上替换的方法添加到_osr_nmethods_head链表上。

    在HotSpot VM的实现里,一般一个类被加载进来之后,里面的方法最早也要到它即将第一次被执行的时候才有可能被JIT编译器所编译。在那之前,非abstract非native的Java方法都只是以Java字节码的形式存在,没有对应的机器码(上面所说的 Method::_code属性的值为NULL)

    如果在调用方法时,会检测Method::_code属性,如果不为空,则执行编译完成后的方法;对于栈上替换来说,会通过之前介绍的lookup_osr_nmethod_for()函数查找到本次编译的结果,然后执行栈上替换操作。

    (1)安装与卸载编译方法

    Method::set_code()函数的实现如下:

    void Method::set_code(methodHandle mh, nmethod *code) {
    
      mh->_code = code;   
    
      int comp_level = code->comp_level();
      if (comp_level > mh->highest_comp_level()) {
        mh->set_highest_comp_level(comp_level);
      }
    
      OrderAccess::storestore();
      mh->_from_compiled_entry = code->verified_entry_point();
      OrderAccess::storestore();
    
      // 如果不是MethodHandle的invoke等方法,即编译代码可以立即执行
      if (!mh->is_method_handle_intrinsic()){
         mh->_from_interpreted_entry = mh->get_i2c_entry();
      }
    }

    所谓“安装”其实就是把nmethod与其对应的Method关联起来,把各入口都设置上。设置Method::_code、Method::_from_compiled_entry和Method::_from_interpreted_entry属性。这3个属性对于编译执行与解释执行非常重要,在后面会详细介绍。

    与“安装”相反的“卸载”逻辑就是抛弃已编译好的机器码,调用的函数如下:

    // 清除编译生成的机器码,返回解释执行
    void Method::clear_code() {
      // _adapter是定义在Method类中的、类型为AdapterHandlerEntry*的变量;
      if (_adapter == NULL) {
        _from_compiled_entry    = NULL;
      } else {
        // 没有编译的版本时,只能回到解释执行
        _from_compiled_entry    = _adapter->get_c2i_entry();
      }
    
      OrderAccess::storestore();
      _from_interpreted_entry = _i2i_entry;
      OrderAccess::storestore();
    
      _code = NULL;
    }

    当nmethod被标记成not_entrant或者zombie时,或者执行CodeCache垃圾回收时会调用如上函数卸载编译的方法。

    (2)安装与卸载OSR

    调用InstanceKlass::add_osr_nmethod()与InstanceKlass::remove_osr_nmethod()函数对OSR进行安装与卸载。

    add_osr_nmethod()函数用于将需要执行栈上替换的nmethod实例插入到InstanceKlass的osr_nmethods链表上,函数的实现如下:

    void InstanceKlass::add_osr_nmethod(nmethod* n) {
      // 获取锁
      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();
     
      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; };
    

    与add_osr_nmethod()函数相对应的就是remove_osr_nmethod()函数,用于从osr_nmethod链表上移除nmethod,当nmethod被标记成not_entrant或者zombie时,或者执行CodeCache垃圾回收时会调用该函数,函数的实现如下:

    void InstanceKlass::remove_osr_nmethod(nmethod* n) {
    
      // 校验是否栈上替换方法
      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) {
          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);
      }
    
    }
    

     公众号 深入剖析Java虚拟机HotSpot 已经更新虚拟机源代码剖析相关文章到60+,欢迎关注,如果有任何问题,可加作者微信mazhimazh,拉你入虚拟机群交流

  • 相关阅读:
    【转】使用scipy进行层次聚类和k-means聚类
    spark wordcont Spark: sortBy和sortByKey函数详解
    scala akka通信机制
    redis AOF 和RDB
    java虚拟机学习
    快速排序,一个爱情故事-java版
    Caused by: java.io.IOException: Added a key not lexically larger than previous.
    java.io.IOException: No FileSystem for scheme: hdfs
    " java.lang.NoSuchFieldError: HBASE_CLIENT_PREFETCH_LIMIT
    用户活跃度分析
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/15877723.html
Copyright © 2020-2023  润新知