SubRoutine

作者: 程序员札记 | 来源:发表于2022-07-17 21:32 被阅读0次

    众所周知jvm有invokestatic,invokedynamic,invokestatic,invokespecial,invokevirtual几条方法调用指令,每个负责调用不同的方法,
    而这些方法调用落实到hotspot上都位于hotspot\src\share\vm\runtime\javaCalls.hpp的JavaCalls :

    1. JavaCalls
    class JavaCalls: AllStatic {
      static void call_helper(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS);
     public:
      // call_special
      // ------------
      // The receiver must be first oop in argument list
      static void call_special(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);
      static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS); // No args
      static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, TRAPS);
      static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
    
      // virtual call
      // ------------
      // The receiver must be first oop in argument list
      static void call_virtual(JavaValue* result, KlassHandle spec_klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);
      static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, Symbol* name, Symbol* signature, TRAPS); // No args
      static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, Symbol* name, Symbol* signature, Handle arg1, TRAPS);
      static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
    
      // Static call
      // -----------
      static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);
      static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS);
      static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, TRAPS);
      static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
      static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, Handle arg3, TRAPS);
    
      // Low-level interface
      static void call(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS);
    };
    

    上面的方法是自解释的,对应各自的invoke*指令,这些call_static,call_virtual内部调用了call()函数:

    void JavaCalls::call(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) {
      assert(THREAD->is_Java_thread(), "only JavaThreads can make JavaCalls");
      os::os_exception_wrapper(call_helper, result, method, args, THREAD);
    }
    

    call()只是简单检查了一下线程信息,以及根据平台比如windows会使用结构化异常(SEH)包裹call_helper,最终执行方法调用的还是call_helper。

    void JavaCalls::call_helper(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) {
      ...
     
      // 如果当前方法为空,则直接返回
      if (method->is_empty_method()) {
        assert(result->get_type() == T_VOID, "an empty method must return a void value");
        return;
      }
    
    
      //根据情况决定是否编译该方法,JIT和-Xcomp都有可能触发它
      CompilationPolicy::compile_if_required(method, CHECK);
    
      // 解释器入口点
      address entry_point = method->from_interpreted_entry();
      if (JvmtiExport::can_post_interpreter_events() && thread->is_interp_only_mode()) {
        entry_point = method->interpreter_entry();
      }
    
      // 确定返回值类型
      BasicType result_type = runtime_type_from(result);
      bool oop_result_flag = (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY);
    
      // 返回值地址
      intptr_t* result_val_address = (intptr_t*)(result->get_value_addr());
    
      // 确定receiver,如果是static函数就没有receiver
      Handle receiver = (!method->is_static()) ? args->receiver() : Handle();
    
      if (!thread->stack_guards_enabled()) {
        thread->reguard_stack();
      }
    
      // 确认当前sp是否到达ShadowPages,即是否会触发栈溢出错误
      address sp = os::current_stack_pointer();
      if (!os::stack_shadow_pages_available(THREAD, method, sp)) {
        // Throw stack overflow exception with preinitialized exception.
        Exceptions::throw_stack_overflow_exception(THREAD, __FILE__, __LINE__, method);
        return;
      } else {
        // Touch pages checked if the OS needs them to be touched to be mapped.
        os::map_stack_shadow_pages(sp);
      }
    
    
      // 执行调用
      { JavaCallWrapper link(method, receiver, result, CHECK);
        { HandleMark hm(thread);  // HandleMark used by HandleMarkCleaner
    
          StubRoutines::call_stub()(
            (address)&link,
            // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
            result_val_address,          // see NOTE above (compiler problem)
            result_type,
            method(),
            entry_point,
            args->parameters(),
            args->size_of_parameters(),
            CHECK
          );
    
          result = link.result();  // circumvent MS C++ 5.0 compiler bug (result is clobbered across call)
          // Preserve oop return value across possible gc points
          if (oop_result_flag) {
            thread->set_vm_result((oop) result->get_jobject());
          }
        }
      } 
    
    
      // 设置返回值
      if (oop_result_flag) {
        result->set_jobject((jobject)thread->vm_result());
        thread->set_vm_result(NULL);
      }
    }
    

    call_helper又可以分为两步,第一步判断一下方法是否为空,是否可以JIT编译,是否还有栈空间可以等,第二步StubRoutines::call_stub()实际调用os+cpu限定的方法。
    这个StubRoutines::call_stub()返回的是一个函数指针,指向的是平台特定的方法,所以这段代码:

          StubRoutines::call_stub()(
            (address)&link,
            // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
            result_val_address,          // see NOTE above (compiler problem)
            result_type,
            method(),
            entry_point,
            args->parameters(),
            args->size_of_parameters(),
            CHECK
          );
    

    call_stub()返回一个函数指针,指向依赖于操作系统和cpu架构的特定的方法,原因很简单,要执行native代码,得看看是什么cpu架构以便确定寄存器,看看什么os以便确定ABI。
    然后传递8个参数到这个方法里面并执行这个方法。那么这个方法是什么呢?进入stubRoutines.cpp便知是StubRoutines::_call_stub_entry

    StubRoutines

    1、定义
    StubRoutines是一个包含一系列编译程序或者JVM运行时系统使用的关键函数的地址的Holder类,其定义在hotspot src/share/vm/runtime/stubRoutines.hpp中。可以通过StubRoutines获取这些函数的内存地址,然后通过指针的方式调用目标函数,如下图:

    image.png

    以JavaCalls::call_helper方法中调用的StubRoutines::call_stub()方法为例说明,该方法返回的其实就是一个函数的地址,如下图:

    image.png

    CallStub就是这个函数的别名,这个其实是解释器执行字节码的终极入口。CAST_TO_FN_PTR宏定义就是完成指针类型转换的,如下图:

    image.png

    2、 initialize1和initialize2方法
    我们重点关注StubRoutines的两个静态初始化方法initialize1和initialize2方法的实现,以此为入口了解该类的用法。这两个方法的调用链如下:

    image.png

    最终都是在init_globals一个方法中调用的,不过initialize1在前,在universe初始化前执行,initialize2在universe初始化完成后执行,init_globals的代码如下:


    image.png

    两者的源码实现说明如下:

    void stubRoutines_init1() { StubRoutines::initialize1(); }
    void stubRoutines_init2() { StubRoutines::initialize2(); }
     
    void StubRoutines::initialize1() {
      if (_code1 == NULL) {
        //ResourceMark的作用类似于HandleMark,两者mark的区域不同,一个是ResourceArea,一个是HandleArea
        ResourceMark rm;
        //跟踪启动时间
        TraceTime timer("StubRoutines generation 1", TraceStartupTime);
        //创建一个保存不会重定位的本地代码的Blob
        _code1 = BufferBlob::create("StubRoutines (1)", code_size1);
        if (_code1 == NULL) {
          //创建失败抛出OOM异常
          vm_exit_out_of_memory(code_size1, OOM_MALLOC_ERROR, "CodeCache: no room for StubRoutines (1)");
        }
        CodeBuffer buffer(_code1);
        //生成字节码解释模板
        StubGenerator_generate(&buffer, false);
      }
    }
     
     
    void StubRoutines::initialize2() {
      if (_code2 == NULL) {
        ResourceMark rm;
        TraceTime timer("StubRoutines generation 2", TraceStartupTime);
        _code2 = BufferBlob::create("StubRoutines (2)", code_size2);
        if (_code2 == NULL) {
          vm_exit_out_of_memory(code_size2, OOM_MALLOC_ERROR, "CodeCache: no room for StubRoutines (2)");
        }
        CodeBuffer buffer(_code2);
        //跟initialize1中不同的是,这里传入的true,其他的都一样
        StubGenerator_generate(&buffer, true);
      }
     
     
       public:
      StubGenerator(CodeBuffer* code, bool all) : StubCodeGenerator(code) {
        if (all) {
          generate_all();
        } else {
          //如果传入false执行的是initial相关的代码
          generate_initial();
        }
      }
    }; // end class declaration
     
    void StubGenerator_generate(CodeBuffer* code, bool all) {
      StubGenerator g(code, all);
    }
    

    下面会逐一分析相关类的用法。

    二、ResourceMark
    ResourceMark和HandleMark用法类似,通过构造方法来保存当前线程的ResourceArea的状态,当方法执行完毕后,通过析构函数及时释放方法执行过程中从ResourceArea中分配的内存,将当前线程的ResourceArea的状态恢复至方法执行前的状态。ResourceMark的定义位于hotspot src/share/vm/memory/resourceArea.hpp中,它定义了四个protected属性,如下:

    • _area:ResourceArea *,关联的ResourceArea实例
    • _chunk:Chunk *,_area当前使用的Chunk
    • _hwm,_max:char *,_area当前使用的Chunk的可分配内存的范围
    • _size_in_bytes:size_t,_area的总的内存大小
      ResourceMark定义的核心方法就只有其构造和析构函数。\

    1、ResourceArea
    ResourceArea跟HandleArea一样继承Area,其核心就是一个方法allocate_bytes,用于分配指定大小的内存,其源码说明如下:

     ResourceArea(MEMFLAGS flags = mtThread) : Arena(flags) {
        debug_only(_nesting = 0;)
      }
     
      ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) : Arena(flags, init_size) {
        debug_only(_nesting = 0;);
      }
     
     
    char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) {
        //调用Area的Amalloc方法分配内存
        return (char*)Amalloc(size, alloc_failmode);
      }
    

    allocate_bytes的调用链如下:

    image.png

    2、构造和析构函数
    构造和析构函数的源码说明如下:

    ResourceMark()               { initialize(Thread::current()); }
     
      ResourceMark( ResourceArea *r ) :
        _area(r), _chunk(r->_chunk), _hwm(r->_hwm), _max(r->_max) {
        //保存ResourceArea的各属性
        _size_in_bytes = r->_size_in_bytes;
        debug_only(_area->_nesting++;)
        assert( _area->_nesting > 0, "must stack allocate RMs" );
      }
     
        void initialize(Thread *thread) {
        //保存线程thread的resource_area的各属性
        _area = thread->resource_area();
        _chunk = _area->_chunk;
        _hwm = _area->_hwm;
        _max= _area->_max;
        _size_in_bytes = _area->size_in_bytes();
        debug_only(_area->_nesting++;)
        assert( _area->_nesting > 0, "must stack allocate RMs" );
      }
     
       ~ResourceMark() {
        assert( _area->_nesting > 0, "must stack allocate RMs" );
        debug_only(_area->_nesting--;)
        //把_area重置到ResourceMark创建时的属性
        reset_to_mark();
      }
     
        void reset_to_mark() {
         //UseMallocOnly表示只使用默认的malloc/free分配内存
        if (UseMallocOnly) free_malloced_objects();
     
        //如果存在下一个Chunk,即创建了新的Chunk
        if( _chunk->next() ) {       // Delete later chunks
          //创建了新Chunk则size_in_bytes变大
          assert(_area->size_in_bytes() > size_in_bytes(), "Sanity check");
          //恢复成原来的大小
          _area->set_size_in_bytes(size_in_bytes());
          //释放当前Chunk以后的所有Chunk
          _chunk->next_chop();
        } else {
          //没有创建新的Chunk,则size_in_bytes不变
          assert(_area->size_in_bytes() == size_in_bytes(), "Sanity check");
        }
        //恢复_area的各种属性
        _area->_chunk = _chunk;     // Roll back arena to saved chunk
        _area->_hwm = _hwm;
        _area->_max = _max;
     
        // 将_hwm和_max之间的内存填充badResourceValue
        if (ZapResourceArea) memset(_hwm, badResourceValue, _max - _hwm);
      }
    

    以get_canonical_path的使用为例说明,如下图:

    image.png

    从上述的实例可知ResourceMark是在使用ResourceArea分配内存时构建,当分配的内存不再使用了就销毁ResourceMark,跟HandleMark在Java方法调用前后创建销毁略有不同。

    三、BufferBlob
    BufferBlob继承自CodeBlob,定义位于hotspot src/share/vm/code/codeBlob.hpp中,用来保存不需要重定向的机器码,如解释器或者stubroutines的机器码,其类继承关系如下图:

    image.png

    BufferBlob没有添加新的属性,主要添加了创建和释放的方法,其源码说明如下:

    BufferBlob* BufferBlob::create(const char* name, int buffer_size) {
       //通过构造和析构函数实现线程的状态转换
       //执行构造函数时从_thread_in_native转换成_thread_in_vm,析构时转换回去
      ThreadInVMfromUnknown __tiv;  // get to VM state in case we block on CodeCache_lock
     
      BufferBlob* blob = NULL;
      unsigned int size = sizeof(BufferBlob);
      //计算待分配的内存大小
      size = align_code_offset(size);
      //round_to是对buffer_size取整,使其等于oopSize的整数倍
      size += round_to(buffer_size, oopSize);
      assert(name != NULL, "must provide a name");
      {
        //获取锁CodeCache_lock
        MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
        //创建一个新的BufferBlob,这里是分两步操作,先调用new分配内存,对分配的内存调用BufferBlob构造方法
        blob = new (size) BufferBlob(name, size);
      }
      //CodeCache_lock释放后记录内存的使用
      MemoryService::track_code_cache_memory_usage();
     
      return blob;
    }
     
    void* BufferBlob::operator new(size_t s, unsigned size, bool is_critical) throw() {
      //从CodeCache中分配指定大小的内存
      void* p = CodeCache::allocate(size, is_critical);
      return p;
    }
     
    BufferBlob::BufferBlob(const char* name, int size)
    : CodeBlob(name, sizeof(BufferBlob), size, CodeOffsets::frame_never_safe, /*locs_size:*/ 0)
    {}
     
     
    BufferBlob* BufferBlob::create(const char* name, CodeBuffer* cb) {
      ThreadInVMfromUnknown __tiv;  // get to VM state in case we block on CodeCache_lock
     
      BufferBlob* blob = NULL;
      //计算待分配的内存大小
      unsigned int size = allocation_size(cb, sizeof(BufferBlob));
      assert(name != NULL, "must provide a name");
      {
        //获取锁CodeCache_lock
        MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
        blob = new (size) BufferBlob(name, size, cb);
      }
      // Track memory usage statistic after releasing CodeCache_lock
      MemoryService::track_code_cache_memory_usage();
     
      return blob;
    }
     
    unsigned int CodeBlob::allocation_size(CodeBuffer* cb, int header_size) {
      //将CodeBuffer的各部分的内存大小相加
      unsigned int size = header_size;
      size += round_to(cb->total_relocation_size(), oopSize);
      // align the size to CodeEntryAlignment
      size = align_code_offset(size);
      size += round_to(cb->total_content_size(), oopSize);
      size += round_to(cb->total_oop_size(), oopSize);
      size += round_to(cb->total_metadata_size(), oopSize);
      return size;
    }
     
    BufferBlob::BufferBlob(const char* name, int size, CodeBuffer* cb)
      : CodeBlob(name, cb, sizeof(BufferBlob), size, CodeOffsets::frame_never_safe, 0, NULL)
    {}
     
     
    void BufferBlob::free( BufferBlob *blob ) {
       //转换线程状态
      ThreadInVMfromUnknown __tiv; 
      // 清空blob的数据
      blob->flush();
      {
        //获取锁CodeCache_lock
        MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
        //释放blob
        CodeCache::free((CodeBlob*)blob);
      }
      // Track memory usage statistic after releasing CodeCache_lock
      MemoryService::track_code_cache_memory_usage();
    }
    

    四、CodeBlob
    1、定义
    CodeBlob是保存在CodeCache中所有实例的基类,跟BufferCode一样位于codeBlob.hpp中,参考BufferBlob中的类继承关系,其中

    -nmethod:表示编译后的Java代码,包含调用本地代码的方法

    • RuntimeStub:JVM运行时的方法
      DeoptimizationBlob:用于去优化
      -ExceptionBlob:用于调用栈回滚
      -SafepointBlob:用于处理非法使用指令导致的易擦航
      定义的属性如下:

    • _name:char*, CodeBlob的名称

    • _size:int,CodeBlob总的内存大小

    • _header_size:int,CodeBlob中header区的大小

    • _relocation_size:int,CodeBlob中relocation区的大小

    • _content_offset:int,content区开始的偏移位置

    • _code_offset:int,指令区开始的偏移位置

    • _frame_complete_offset:int,_frame_complete指令的偏移位置

    • _data_offset:int,data区的偏移位置

    • _frame_size:int,栈帧的大小

    • _oop_maps:OopMapSet*,关联的OopMapSet

    • _strings: CodeStrings,关联的代码字符串

    CodeBlob整体上在内存中划分为几个区的,如下图,其中content部分包含常量,指令等

    image.png

    CodeBlob定义的方法可以分为以下几种:

    • 获取当前CodeBlob的类型,如is_buffer_blob,is_nmethod,is_runtime_stub等,都是虚方法,默认返回false
    • 获取CodeBlob中各部分内存的边界,如header_begin,header_end,relocation_begin,relocation_end等
    • 获取CodeBlob中各部分内存的大小,如header_size,relocation_size,content_size等
    • 获取CodeBlob中各部分内存的起始偏移量,如relocation_offset,data_offset等
    • 判断某部分内存是否包含某个指针,如blob_contains,relocation_contains,code_contains等
    • 其他属性相关的方法,如oop_maps,oop_map_for_return_address,frame_size,name等

    2、构造和flush方法
    BufferBlob中用到CodeBlob的构造方法和flush方法的源码说明如下:

    CodeBlob::CodeBlob(const char* name, int header_size, int size, int frame_complete, int locs_size) {
     
      //校验size等是经过round_to取整后的
      assert(size        == round_to(size,        oopSize), "unaligned size");
      assert(locs_size   == round_to(locs_size,   oopSize), "unaligned size");
      assert(header_size == round_to(header_size, oopSize), "unaligned size");
      assert(!UseRelocIndex, "no space allocated for reloc index yet");
     
      //设置各属性
      _name                  = name;
      _size                  = size;
      _frame_complete_offset = frame_complete;
      _header_size           = header_size;
      _relocation_size       = locs_size;
      _content_offset        = align_code_offset(header_size + _relocation_size);
      _code_offset           = _content_offset;
      _data_offset           = size;
      _frame_size            =  0;
      set_oop_maps(NULL);
    }
     
    CodeBlob::CodeBlob(
      const char* name,
      CodeBuffer* cb,
      int         header_size,
      int         size,
      int         frame_complete,
      int         frame_size,
      OopMapSet*  oop_maps
    ) {
     //校验size等是经过round_to取整后的
      assert(size        == round_to(size,        oopSize), "unaligned size");
      assert(header_size == round_to(header_size, oopSize), "unaligned size");
     
      _name                  = name;
      _size                  = size;
      _frame_complete_offset = frame_complete;
      _header_size           = header_size;
       //从CodeBuffer中取出相关属性,设置到CodeBlob
      _relocation_size       = round_to(cb->total_relocation_size(), oopSize);
      _content_offset        = align_code_offset(header_size + _relocation_size);
      _code_offset           = _content_offset + cb->total_offset_of(cb->insts());
      _data_offset           = _content_offset + round_to(cb->total_content_size(), oopSize);
      assert(_data_offset <= size, "codeBlob is too small");
      //从CodeBuffer拷贝内存数据到CodeBlob中
      cb->copy_code_and_locs_to(this);
      set_oop_maps(oop_maps);
      _frame_size = frame_size;
     
    }
     
    void CodeBlob::flush() {
      if (_oop_maps) {
        //释放堆内存
        FREE_C_HEAP_ARRAY(unsigned char, _oop_maps, mtCode);
        _oop_maps = NULL;
      }
      //释放CodeString
      _strings.free();
    }
    

    五、CodeBuffer
    1、定义
    CodeBuffer类似于IO里面的BufferedReader等用来临时缓存生成的汇编代码,CodeBuffer用来缓存汇编代码的内存通常是BufferBlob中content部分的内存,也可以是其他的已分配的一片内存,可参考CodeBuffer的构造函数,其定义位于hotspot src/share/vm/asm/codeBuffer.hpp中。CodeBuffer有两种变种,一种是引用一片用来生成静态代码的内存,这种代码没有任何重定向信息;一种是引用CodeBuffer创建时创建的内存,用于nmethod生成汇编代码。CodeBuffer的内存可以被划分成几个部分,每个部分称之为section,每个section都是独立增加汇编代码的,可以不断增长的。

    CodeBuffer定义的私有属性如下:

    • _name: const char*,CodeBuffer的名称
    • _consts:CodeSection,常量,指令跳转部分
    • _insts:CodeSection,汇编指令部分
    • _stubs:CodeSection,异常处理的回调地址等
    • _before_expand:CodeBuffer*,扩展前的CodeBuffer
    • _blob:BufferBlob*,可选的用于缓存生成的代码
    • _total_start:address,缓存代码的内存的起始地址
    • _total_size:csize_t,缓存代码的内存的大小
    • _oop_recorder:OopRecorder*,用于记录和检索oop的
    • _code_strings:CodeStrings,汇编代码字符串
    • _default_oop_recorder:OopRecorder,覆盖初始的initialize_oop_recorder

    CodeBuffer定义了一个表示其内存布局的枚举,也可通过此枚举完成内存各部分的遍历,如下图:

    CodeBuffer定义的public方法主要有以下几类:

    • 用来查找操作section的,如code_section,code_section_name,section_index_of等
    • 返回私有属性相关的,如consts,name,before_expand,blob,set_blob,oop_recorder等
    • 获取表示汇编指令的_insts的属性相关的,如insts_begin,insts_limit,insts_mark,insts_size,insts_remaining,relocate等
    • 获取某部分的内存大小,如total_content_size,total_offset_of,total_relocation_size,total_oop_size等

    2、构造和析构方法
    构造和析构方法的源码说明如下:

    CodeBuffer(address code_start, csize_t code_size) {
        assert(code_start != NULL, "sanity");
        initialize_misc("static buffer");
        initialize(code_start, code_size);
        verify_section_allocation();
      }
     
       void initialize_misc(const char * name) {
        // 各属性初始化
        assert(name != NULL, "must have a name");
        _name            = name;
        _before_expand   = NULL;
        _blob            = NULL;
        _oop_recorder    = NULL;
        _decode_begin    = NULL;
        _overflow_arena  = NULL;
      }
     
      void initialize(address code_start, csize_t code_size) {
         //各section初始化
        _consts.initialize_outer(this,  SECT_CONSTS);
        _insts.initialize_outer(this,   SECT_INSTS);
        _stubs.initialize_outer(this,   SECT_STUBS);
        _total_start = code_start;
        _total_size  = code_size;
        // Initialize the main section:
        _insts.initialize(code_start, code_size);
        assert(!_stubs.is_allocated(),  "no garbage here");
        assert(!_consts.is_allocated(), "no garbage here");
        _oop_recorder = &_default_oop_recorder;
      }
     
    void CodeBuffer::verify_section_allocation() {
      //tstart表示内存的起始地址,tend表示结束地址
      address tstart = _total_start;
      if (tstart == badAddress)  return;  // smashed by set_blob(NULL)
      address tend   = tstart + _total_size;
      if (_blob != NULL) {
        //确保blob的content部分的内存在CodeBuffer的内存范围内
        guarantee(tstart >= _blob->content_begin(), "sanity");
        guarantee(tend   <= _blob->content_end(),   "sanity");
      }
      // 逐一遍历各section
      for (int n = (int) SECT_FIRST; n < (int) SECT_LIMIT; n++) {
        CodeSection* sect = code_section(n);
        //section为空或者未分配表示未填充对应的数据
        if (!sect->is_allocated() || sect->is_empty())  continue;
        //section不为空,其起始地址是按照alignment取整的
        guarantee((intptr_t)sect->start() % sect->alignment() == 0
               || sect->is_empty() || _blob == NULL,
               "start is aligned");
        for (int m = (int) SECT_FIRST; m < (int) SECT_LIMIT; m++) {
          CodeSection* other = code_section(m);
          if (!other->is_allocated() || other == sect)  continue;
          //确保其他section与当前section的内存没有重叠的
          guarantee(!other->contains(sect->start()    ), "sanity");
          guarantee(!other->contains(sect->limit() - 1), "sanity");
        }
        //确保section的结束地址小于CodeBuffer的结束地址
        guarantee(sect->end() <= tend, "sanity");
        guarantee(sect->end() <= sect->limit(), "sanity");
      }
    }
     
    CodeBuffer::CodeBuffer(CodeBlob* blob) {
      initialize_misc("static buffer");
      //利用目标blob的content的起始地址和大小完成CodeBuffer的section初始化
      initialize(blob->content_begin(), blob->content_size());
      verify_section_allocation();
    }
     
    //惰性初始化版本
     CodeBuffer(const char* name) {
        initialize_misc(name);
      }
     
        CodeBuffer(const char* name, csize_t code_size, csize_t locs_size) {
        initialize_misc(name);
        initialize(code_size, locs_size);
      }
     
    void CodeBuffer::initialize(csize_t code_size, csize_t locs_size) {
      // Compute maximal alignment.
      int align = _insts.alignment();
      //slop表示每个section的间隔
      int slop = (int) CodeSection::end_slop();
     
      assert(blob() == NULL, "only once");
      //注意这里用了乘法,即为每个section都分配了内存
      set_blob(BufferBlob::create(_name, code_size + (align+slop) * (SECT_LIMIT+1)));
      //blob分配失败,抛出异常
      if (blob() == NULL) {
        // The assembler constructor will throw a fatal on an empty CodeBuffer.
        return;  // caller must test this
      }
     
      //section初始化
      initialize(_total_start, _total_size);
     
      assert((uintptr_t)insts_begin() % CodeEntryAlignment == 0, "instruction start not code entry aligned");
     
      pd_initialize();
     
      if (locs_size != 0) {
        //初始化locs
        _insts.initialize_locs(locs_size / sizeof(relocInfo));
      }
     
      verify_section_allocation();
    }
     
      CodeBuffer::~CodeBuffer() {
      verify_section_allocation();
     
      //往前遍历找到扩展前的CodeBuffer
      for (CodeBuffer* cb = this; cb != NULL; cb = cb->before_expand()) {
        //释放CodeBuffer对应的blob
        cb->free_blob();
      }
     
      //释放_overflow_arena对应的内存
      delete _overflow_arena;
     
      //释放_code_strings
      free_strings();
     
    }
     
    void CodeBuffer::set_blob(BufferBlob* blob) {
      _blob = blob;
      if (blob != NULL) {
        address start = blob->content_begin();
        address end   = blob->content_end();
        //对起始地址取整,这个是根据blob的content的begin和end计算CodeBuffer的start和size
        int align = _insts.alignment();
        start += (-(intptr_t)start) & (align-1);
        _total_start = start;
        _total_size  = end - start;
      } else {
     
    }
     
    void CodeBuffer::free_blob() {
      if (_blob != NULL) {
        BufferBlob::free(_blob);
        set_blob(NULL);
      }
    }
     
    void free_strings() {
        if (!_code_strings.is_null()) {
          _code_strings.free(); // sets _strings Null as a side-effect.
        }
      }
    3、copy_code_and_locs_to方法
         copy_code_and_locs_to方法用于将CodeBuffe中的汇编代码和重定向数据拷贝到一个新的Blob中,其源码说明如下:
    
    void copy_code_and_locs_to(CodeBlob* blob) {
        assert(blob != NULL, "sane");
        //拷贝重定向section
        copy_relocations_to(blob);
        //拷贝inst汇编指令section
        copy_code_to(blob);
      }
     
      void CodeBuffer::copy_code_to(CodeBlob* dest_blob) {
      //利用目标dest_blob构造一个新的CodeBuffer
      CodeBuffer dest(dest_blob);
      //校验CodeBlob的content部分的大小大于CodeBuffer的大小
      assert(dest_blob->content_size() >= total_content_size(), "good sizing");
       //根据当前CodeBuffer的各section的属性初始化dest中各section的属性
      this->compute_final_layout(&dest);
      //拷贝代码
      relocate_code_to(&dest);
     
      //设置codeString
      dest_blob->set_strings(_code_strings);
     
      //检验dest代码拷贝的结果是否正确
      assert(round_to(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity");
     
      //刷新指令缓存
      ICache::invalidate_range(dest_blob->code_begin(), dest_blob->code_size());
    }
     
     
    void CodeBuffer::compute_final_layout(CodeBuffer* dest) const {
      address buf = dest->_total_start;
      csize_t buf_offset = 0;
      //校验目标CodeBuffer的空间足够大
      assert(dest->_total_size >= total_content_size(), "must be big enough");
     
      {
        // not sure why this is here, but why not...
        int alignSize = MAX2((intx) sizeof(jdouble), CodeEntryAlignment);
        assert( (dest->_total_start - _insts.start()) % alignSize == 0, "copy must preserve alignment");
      }
     
      const CodeSection* prev_cs      = NULL;
      CodeSection*       prev_dest_cs = NULL;
     
      //逐一处理原CodeBuffer和新CodeBuffer的各section
      for (int n = (int) SECT_FIRST; n < (int) SECT_LIMIT; n++) {
        //cs表示原CodeBuffer的section
        const CodeSection* cs = code_section(n);
        csize_t csize = cs->size();
        //dest_cs表示新CodeBuffer的section
        CodeSection* dest_cs = dest->code_section(n);
        //如果cs不为空
        if (!cs->is_empty()) {
          // Compute initial padding; assign it to the previous non-empty guy.
          // Cf. figure_expanded_capacities.
          csize_t padding = cs->align_at_start(buf_offset) - buf_offset;
          if (padding != 0) {
            buf_offset += padding;
            assert(prev_dest_cs != NULL, "sanity");
            prev_dest_cs->_limit += padding;
          }
          prev_dest_cs = dest_cs;
          prev_cs      = cs;
        }
     
        debug_only(dest_cs->_start = NULL);  
        //dest_cs的初始化
        dest_cs->initialize(buf+buf_offset, csize);
        dest_cs->set_end(buf+buf_offset+csize);
        assert(dest_cs->is_allocated(), "must always be allocated");
        assert(cs->is_empty() == dest_cs->is_empty(), "sanity");
     
        buf_offset += csize;
      }
     
      // Done calculating sections; did it come out to the right end?
      assert(buf_offset == total_content_size(), "sanity");
      dest->verify_section_allocation();
    }
    

    六、CodeSection
    1、定义
    CodeSection表示一个用来保存汇编代码和其关联的重定向表的内存区,CodeBuffer中有几个CodeSection,他们可以被并发的填充且尾部是连起来的。CodeSection同样定义在codeBuffer.hpp中,其包含私有属性如下:

    • _start:address,汇编指令的开始地址
    • _mark:address,用户打标的一个地址
    • _end:address,汇编指令的结束地址
    • _limit:address,允许填充汇编指令的地址下限
    • _locs_start: relocInfo* ,重定向信息的起始地址
    • _locs_end: relocInfo* ,重定向信息的结束地址
    • _locs_limit: relocInfo* ,保存重定向信息的内存的下限
    • _locs_point: address,上一次重定向的地址
    • _locs_own:bool,是否自己分配一个locs
    • _frozen:bool,是否冻结了
    • _index:int,标识所属的section的类型
    • _outer:CodeBuffer*,关联的外层的CodeBuffer

    其描述的内存结构如下图所示:

    image.png

    CodeSection定义的方法可以分为以下几种:

    • 读写属性相关的,如start,end,set_end,locs_start,locs_end,index,has_locs等
    • 初始化相关的,如initialize_outer,initialize,initialize_locs,initialize_locs_from,initialize_shared_locs等
    • Code emission时用来改写_end的方法,如emit_int8,emit_int16,emit_float,emit_address等

    2、initialize
    重点关注initialize相关方法的实现,其源码说明如下:

     //初始化_consts section调用
     void initialize_outer(CodeBuffer* outer, int index) {
       _outer = outer;
       _index = index;
     }
     
     //初始化_insts section调用
     void initialize(address start, csize_t size = 0) {
       assert(_start == NULL, "only one init step, please");
       _start         = start;
       _mark          = NULL;
       _end           = start;
    
       _limit         = start + size;
       _locs_point    = start;
     }
    
    //CodeBuffer::initialize(csize_t code_size, csize_t locs_size) 时调用
     void CodeSection::initialize_locs(int locs_capacity) {
     assert(_locs_start == NULL, "only one locs init step, please");
    
     csize_t min_locs = MAX2(size() / 16, (csize_t)4);
     //不能低于最低值
     if (locs_capacity < min_locs)  locs_capacity = min_locs;
     //NEW_RESOURCE_ARRAY是一个宏定义,从ResourceArea中分配内存
     relocInfo* locs_start = NEW_RESOURCE_ARRAY(relocInfo, locs_capacity);
     _locs_start    = locs_start;
     _locs_end      = locs_start;
     _locs_limit    = locs_start + locs_capacity;
     _locs_own      = true;
    }
    
    
    #define NEW_RESOURCE_ARRAY(type, size)\
     (type*) resource_allocate_bytes((size) * sizeof(type))
    
    

    相关文章

      网友评论

        本文标题:SubRoutine

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