jvm

SubRoutine

2022-07-17  本文已影响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属性,如下:

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代码,包含调用本地代码的方法

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

image.png

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

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定义的私有属性如下:

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

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

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中,其包含私有属性如下:

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

image.png

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

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

上一篇下一篇

猜你喜欢

热点阅读