美文网首页
Java Hotspot虚拟机的模板解释器

Java Hotspot虚拟机的模板解释器

作者: buzzerrookie | 来源:发表于2019-03-03 10:23 被阅读0次

众所周知,Java字节码是解释执行的,本文简要分析解释执行的原理。

解释器

Java 8的Hotspot虚拟机有两种解释器,C++解释器(CppInterpreter)和模板解释器(TemplateInterpreter)。

  • C++解释器完全是利用软件循环和case-swicth形式解释执行字节码,速度较慢但移植性好;
  • 模板解释器是默认使用的解释器,它为每种字节码指令都生成了汇编代码,使用机器栈和寄存器,速度比C++解释器快10倍[1]OpenJDK的文档也指出了这种设计的优势。

编译Hotspot时,如果CC_INTERP宏有定义,那么使用C++解释器,否则使用模板解释器。本系列文章均使用模板解释器作为分析对象。
初始化虚拟机时,JNI_CreateJavaVM函数调用了Threads类的create_vm静态函数创建了VMThread,然后create_vm静态函数调用init_globals函数初始化全局模块,接着init_globals函数调用了interpreter_init函数,interpreter_init函数最后调用Interpreter::initialize函数初始化解释器。

Interpreter类

Interpreter类是对两种解释器的包装类,定义在文件hotspot/src/share/vm/interpreter/interpreter.hpp中,其代码如下所示:

#ifdef CC_INTERP
#define CC_INTERP_ONLY(code) code
#define NOT_CC_INTERP(code)
#else
#define CC_INTERP_ONLY(code)
#define NOT_CC_INTERP(code) code
#endif // CC_INTERP

class Interpreter: public CC_INTERP_ONLY(CppInterpreter) NOT_CC_INTERP(TemplateInterpreter) {

  public:
  // Debugging/printing
  static InterpreterCodelet* codelet_containing(address pc)     { return (InterpreterCodelet*)_code->stub_containing(pc); }
#ifdef TARGET_ARCH_x86
# include "interpreter_x86.hpp"
#endif
#ifdef TARGET_ARCH_sparc
# include "interpreter_sparc.hpp"
#endif
#ifdef TARGET_ARCH_zero
# include "interpreter_zero.hpp"
#endif
#ifdef TARGET_ARCH_arm
# include "interpreter_arm.hpp"
#endif
#ifdef TARGET_ARCH_ppc
# include "interpreter_ppc.hpp"
#endif

};

CC_INTERP_ONLY和NOT_CC_INTERP是宏,因为没有定义CC_INTERP宏,所以Interpreter类继承自TemplateInterpreter类。初始化过程中调用Interpreter::initialize函数是由基类TemplateInterpreter完成的,TemplateInterpreter::initialize函数代码如下:

void TemplateInterpreter::initialize() {
  if (_code != NULL) return;
  // assertions
  assert((int)Bytecodes::number_of_codes <= (int)DispatchTable::length,
         "dispatch table too small");

  AbstractInterpreter::initialize();

  TemplateTable::initialize();

  // generate interpreter
  { ResourceMark rm;
    TraceTime timer("Interpreter generation", TraceStartupTime);
    int code_size = InterpreterCodeSize;
    NOT_PRODUCT(code_size *= 4;)  // debug uses extra interpreter code space
    _code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,
                          "Interpreter");
    InterpreterGenerator g(_code);
    if (PrintInterpreter) print();
  }

  // initialize dispatch table
  _active_table = _normal_table;
}
  • TemplateTable::initialize函数为每种Java字节码指令定义了汇编代码模板;
  • 生成StubQueue;
  • InterpreterGenerator g(_code)这个构造函数调用则利用解释器生成器创建了解释器。

模板与模板表

在模板解释器中,模板保存了为字节码生成汇编代码时用到的相关信息,模板表则是模板的集合,保存了所有字节码的模板。
模板Template类和模板表TemplateTable类均定义在文件hotspot/src/share/vm/interpreter/templateTable.hpp中,公共实现在文件hotspot/src/share/vm/interpreter/templateTable.cpp中,部分实现与CPU架构有关,如x86_64的CPU在文件hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp中。
回到TemplateTable::initialize函数,其部分代码如下所示:

void TemplateTable::initialize() {
  if (_is_initialized) return;
  // Initialize table
  TraceTime timer("TemplateTable initialization", TraceStartupTime);
  _bs = Universe::heap()->barrier_set();

  // For better readability
  const char _    = ' ';
  const int  ____ = 0;
  const int  ubcp = 1 << Template::uses_bcp_bit;
  const int  disp = 1 << Template::does_dispatch_bit;
  const int  clvm = 1 << Template::calls_vm_bit;
  const int  iswd = 1 << Template::wide_bit;
  //                                    interpr. templates
  // Java spec bytecodes                ubcp|disp|clvm|iswd  in    out   generator             argument
  def(Bytecodes::_nop                 , ____|____|____|____, vtos, vtos, nop                 ,  _           );
  def(Bytecodes::_aconst_null         , ____|____|____|____, vtos, atos, aconst_null         ,  _           );
  def(Bytecodes::_iconst_m1           , ____|____|____|____, vtos, itos, iconst              , -1           );
  def(Bytecodes::_iconst_0            , ____|____|____|____, vtos, itos, iconst              ,  0           );
  def(Bytecodes::_iconst_1            , ____|____|____|____, vtos, itos, iconst              ,  1           );
  // 省略一些代码, 对每个Java字节码都有def调用
  // wide Java spec bytecodes
  def(Bytecodes::_iload               , ubcp|____|____|iswd, vtos, itos, wide_iload          ,  _           );
  def(Bytecodes::_lload               , ubcp|____|____|iswd, vtos, ltos, wide_lload          ,  _           );
  def(Bytecodes::_fload               , ubcp|____|____|iswd, vtos, ftos, wide_fload          ,  _           );
  def(Bytecodes::_dload               , ubcp|____|____|iswd, vtos, dtos, wide_dload          ,  _           );
  // 省略一些代码
  // JVM bytecodes
  def(Bytecodes::_fast_agetfield      , ubcp|____|____|____, atos, atos, fast_accessfield    ,  atos        );
  def(Bytecodes::_fast_bgetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );
  def(Bytecodes::_fast_cgetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );
  def(Bytecodes::_fast_dgetfield      , ubcp|____|____|____, atos, dtos, fast_accessfield    ,  dtos        );
  // 省略一些代码
  // platform specific bytecodes
  pd_initialize();
  _is_initialized = true;
}

initialize函数在内部为每种字节码都调用def函数以初始化该字节码的模板。

def函数

def函数有多种重载形式,限于篇幅以下只展示两种:

void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(), char filler) {
  assert(filler == ' ', "just checkin'");
  def(code, flags, in, out, (Template::generator)gen, 0);
}

void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(int arg), int arg) {
  // should factor out these constants
  const int ubcp = 1 << Template::uses_bcp_bit;
  const int disp = 1 << Template::does_dispatch_bit;
  const int clvm = 1 << Template::calls_vm_bit;
  const int iswd = 1 << Template::wide_bit;
  // determine which table to use
  bool is_wide = (flags & iswd) != 0;
  // make sure that wide instructions have a vtos entry point
  // (since they are executed extremely rarely, it doesn't pay out to have an
  // extra set of 5 dispatch tables for the wide instructions - for simplicity
  // they all go with one table)
  assert(in == vtos || !is_wide, "wide instructions have vtos entry point only");
  Template* t = is_wide ? template_for_wide(code) : template_for(code);
  // setup entry
  t->initialize(flags, in, out, gen, arg);
  assert(t->bytecode() == code, "just checkin'");
}
  • code参数即为字节码枚举;
  • gen函数指针指向为code表示的字节码生成汇编代码的函数,以iconst_0指令为例,为其生成汇编代码的函数是TemplateTable::iconst:
    void TemplateTable::iconst(int value) {
      transition(vtos, itos);
      if (value == 0) {
        __ xorl(rax, rax);
      } else {
        __ movl(rax, value);
      }
    }
    
  • in和out参数表示栈顶状态(Top-of-Stack),类型是TosState枚举;
  • arg参数即字节码指令所需参数;
  • is_wide与wide指令有关;
  • 模板表内部有一个模板数组(_template_table成员变量)保存所有字节码的模板(Template类),利用字节码枚举值作为数组索引即可获得某个字节码的模板。def函数最后调用模板的initialize函数将生成汇编代码时用到的相关信息保存到模板数组的每个模板。

InterpreterGenerator类

解释器是在虚拟机初始化过程中由解释器生成器动态生成的。与Interpreter类相似,InterpreterGenerator类是对两种解释器生成器的包装类,定义在文件hotspot/src/share/vm/interpreter/interpreterGenerator.hpp中,其代码如下所示:

class InterpreterGenerator: public CC_INTERP_ONLY(CppInterpreterGenerator)
                                   NOT_CC_INTERP(TemplateInterpreterGenerator) {

public:

InterpreterGenerator(StubQueue* _code);

#ifdef TARGET_ARCH_x86
# include "interpreterGenerator_x86.hpp"
#endif
#ifdef TARGET_ARCH_sparc
# include "interpreterGenerator_sparc.hpp"
#endif
#ifdef TARGET_ARCH_zero
# include "interpreterGenerator_zero.hpp"
#endif
#ifdef TARGET_ARCH_arm
# include "interpreterGenerator_arm.hpp"
#endif
#ifdef TARGET_ARCH_ppc
# include "interpreterGenerator_ppc.hpp"
#endif

};

CC_INTERP_ONLY和NOT_CC_INTERP宏的含义同上,InterpreterGenerator类继承自TemplateInterpreterGenerator类,TemplateInterpreterGenerator类则继承了AbstractInterpreterGenerator类。
InterpreterGenerator类的构造函数在文件hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp中实现,代码如下:

InterpreterGenerator::InterpreterGenerator(StubQueue* code)
  : TemplateInterpreterGenerator(code) {
   generate_all(); // down here so it can be "virtual"
}

TemplateInterpreterGenerator::TemplateInterpreterGenerator(StubQueue* _code): AbstractInterpreterGenerator(_code) {
  _unimplemented_bytecode    = NULL;
  _illegal_bytecode_sequence = NULL;
}

AbstractInterpreterGenerator::AbstractInterpreterGenerator(StubQueue* _code) {
  _masm                      = NULL;
}

显然,类体系的构造函数执行完毕后会调用TemplateInterpreterGenerator类的generate_all函数。

generate_all函数

generate_all函数在文件hotspot/src/share/vm/interpreter/templateInterpreter.cpp中实现,限于篇幅仅列出其部分代码:

void TemplateInterpreterGenerator::generate_all() {
  AbstractInterpreterGenerator::generate_all();

  { CodeletMark cm(_masm, "error exits");
    _unimplemented_bytecode    = generate_error_exit("unimplemented bytecode");
    _illegal_bytecode_sequence = generate_error_exit("illegal bytecode sequence - method not verified");
  }

#ifndef PRODUCT
  if (TraceBytecodes) {
    CodeletMark cm(_masm, "bytecode tracing support");
    Interpreter::_trace_code =
      EntryPoint(
        generate_trace_code(btos),
        generate_trace_code(ztos),
        generate_trace_code(ctos),
        generate_trace_code(stos),
        generate_trace_code(atos),
        generate_trace_code(itos),
        generate_trace_code(ltos),
        generate_trace_code(ftos),
        generate_trace_code(dtos),
        generate_trace_code(vtos)
      );
  }
#endif // !PRODUCT

  { CodeletMark cm(_masm, "return entry points");
    const int index_size = sizeof(u2);
    for (int i = 0; i < Interpreter::number_of_return_entries; i++) {
      Interpreter::_return_entry[i] =
        EntryPoint(
          generate_return_entry_for(itos, i, index_size),
          generate_return_entry_for(itos, i, index_size),
          generate_return_entry_for(itos, i, index_size),
          generate_return_entry_for(itos, i, index_size),
          generate_return_entry_for(atos, i, index_size),
          generate_return_entry_for(itos, i, index_size),
          generate_return_entry_for(ltos, i, index_size),
          generate_return_entry_for(ftos, i, index_size),
          generate_return_entry_for(dtos, i, index_size),
          generate_return_entry_for(vtos, i, index_size)
        );
    }
  }

  // 省略一些代码
}

从代码可以看到generate_all函数内部调用了很多其他generate函数,这些函数的返回值都保存到了TemplateInterpreterGenerator的成员变量中,它们的类型要么是address(其实是unsigned char指针),要么是EntryPoint(内部是address数组)。这些generate函数的作用是为一些公用部分生成汇编代码:

  • 未实现或非法的字节码指令:generate_error_exit;
  • 方法调用返回到解释器后:generate_return_entry_for;
  • 异常处理程序:generate_throw_exception,
  • 抛出ArrayIndexOutOfBoundsException:generate_ArrayIndexOutOfBounds_handler;
  • 抛出ArrayStoreException:generate_klass_exception_handler;
  • 抛出ArithmeticException和NullPointerException:generate_exception_handler;
  • 抛出ClassCastException:generate_ClassCastException_handler;
  • 抛出StackOverflowError:generate_StackOverflowError_handler;
  • 方法调用:generate_method_entry;
  • 所有字节码:set_entry_points_for_all_bytes;
  • 其他。

可以利用-XX:+TraceBytecodes选项跟踪字节码。

运行过程

TODO

参考文献

[1] Andrew Dinn. OpenJDK Architecture. http://www.dcs.gla.ac.uk/~jsinger/pdfs/sicsa_openjdk/OpenJDKArchitecture.pdf. 2014
[2] https://www.cs.princeton.edu/picasso/mats/HotspotOverview.pdf
[3] http://www.importnew.com/17072.html

相关文章

网友评论

      本文标题:Java Hotspot虚拟机的模板解释器

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