HotSpot解释器及解释器生成器 [Inside HotSpot] 模板解释器

解释器是一堆本地代码例程构造的,这些例程会在虚拟机启动的时候写入,以后解释就只需要进入指定例程即可。

 解释器总体的架构如下:

1、抽象解释器

class AbstractInterpreter{
StubQueue* _code                      
address    _slow_signature_handler;
address    _entry_table[n];         
address    _cds_entry_table[n];
 ...
}; 

_code属性是一个队列,如下图。

 队列中的InterpreterCodelet表示一个小例程,比如iconst_1对应的代码,invokedynamic对应的代码,异常处理对应的代码,方法入口点对应的代码,这些代码都是一个个InterpreterCodelet...整个解释器都是由这些小块代码例程组成的,每个小块例程完成解释器的部分功能,以此实现整个解释器。

_entry_table也是个重要的属性,这个数组表示方法的例程,比如普通方法是入口点1_entry_table[0],带synchronized的方法是入口点2_entry_table[1],这些_entry_table[0],_entry_table[1]指向的就是之前_code队列里面的小块例程,就像这样:

_entry_table[0] = _code->get_stub("iconst_1")->get_address();
_entry_table[1] = _code->get_stub("fconst_1")->get_address();

  

2、模板解释器

抽象解释器定义了必要的例程,具体的解释器在这之上还有自己的特设的例程。模板解释器就是一个例子,它继承自抽象解释器,在那些例程之上还有自己的特设例程:

  // 数组越界异常例程
  static address    _throw_ArrayIndexOutOfBoundsException_entry;    
  // 数组存储异常例程    
  static address    _throw_ArrayStoreException_entry;  
  // 算术异常例程
  static address    _throw_ArithmeticException_entry;
  // 类型转换异常例程
  static address    _throw_ClassCastException_entry;
  // 空指针异常例程
  static address    _throw_NullPointerException_entry;
  // 抛异常公共例程
  static address    _throw_exception_entry;             

解释器是一堆本地代码例程构造的,这些例程会在虚拟机启动的时候写入,以后解释就只需要进入指定例程即可。

3、解释器生成器

前面刻意说道解释器布局就是想突出它只是一个骨架,要得到可运行的解释器还需要解释器生成器填充这个骨架。

解释器生成器本来可以独自完成填充工作,可能为了解耦,也可能是为了结构清晰,hotspot将字节码的例程抽了出来放到了templateTable(模板表)中,它辅助模板解释器生成器(templateInterpreterGenerator)完成各例程填充。

只有这两个还不能完成任务,因为组成模板解释器的是本地代码例程,本地代码例程依赖于操作系统和CPU,这部分代码位于hotspot/cpu/x86/中,所以

templateInterpreter = 
    templateTable + 
    templateTable_x86 +
    templateInterpreterGenerator + 
    templateInterpreterGenerator_x86 +
    templateInterpreterGenerator_x86_64

例如模板解释器扩展了抽象解释器,它有一个数组越界异常例程:  

// 解释器生成器
// hotspot\share\interpreter\templateInterpreterGenerator.cpp
void TemplateInterpreterGenerator::generate_all() {
    ...
  { CodeletMark cm(_masm, "throw exception entrypoints");
    // 调用CPU相关的代码生成例程
    Interpreter::_throw_ArrayIndexOutOfBoundsException_entry = generate_ArrayIndexOutOfBounds_handler();
  }
  ...
}
// 解释器生成器中CPU相关的部分
// hotspot\os\x86\templateInterpreterGenerator_x86.cpp
address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler() {
  address entry = __ pc();
  __ empty_expression_stack();
  // rarg是数组越界的对象,rbx是越界的索引
  Register rarg = NOT_LP64(rax) LP64_ONLY(c_rarg1);
  __ call_VM(noreg,
             CAST_FROM_FN_PTR(address,
                              InterpreterRuntime::
                              throw_ArrayIndexOutOfBoundsException),
             rarg, rbx);
  return entry;
}
// 解释器运行时
// hotspot\share\interpreter\interpreterRuntime.cpp
IRT_ENTRY(void, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException(JavaThread* thread, arrayOopDesc* a, jint index))
  ResourceMark rm(thread);
  stringStream ss;
  ss.print("Index %d out of bounds for length %d", index, a->length());
  THROW_MSG(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), ss.as_string());
IRT_END

解释器生成器会调用CPU相关的generate_ArrayIndexOutOfBounds_handler()生成异常处理例程,里面有个call_VM,它调用了解释器运行时(InterpreterRuntime)来处理异常。解释器运行时是C++代码,
之所以用它是因为异常处理比较麻烦,还需要C++其他模块的支持(比如这里的stringStream和THROW_MSG),直接生成机器码会非常麻烦,我们可以调用解释器运行时相对轻松的处理。

参考文章:

(1)[Inside HotSpot] 模板解释器 https://www.cnblogs.com/kelthuzadx/p/10707504.html

  

猜你喜欢

转载自www.cnblogs.com/mazhimazhi/p/11442750.html