在解释执行的情况下需要一些类来支持代码生成的过程。

1、InterpreterCodelet与Stub类

Stub类的定义如下:

class Stub VALUE_OBJ_CLASS_SPEC {
 public:

  // General info/converters
  int     size() const       { ShouldNotCallThis(); return 0; }      // must return the size provided by initialize

  // Code info
  address code_begin() const { ShouldNotCallThis(); return NULL; }   // points to the first byte of    the code
  address code_end() const   { ShouldNotCallThis(); return NULL; }   // points to the first byte after the code
};

InterpreterCodelet类的定义如下:

class InterpreterCodelet: public Stub {
 private:
  int                _size;         // the size in bytes
  const char*        _description;  // a description of the codelet, for debugging & printing
  Bytecodes::Code    _bytecode;     // associated bytecode if any

 public:

  // Code info
  address code_begin() const  {
     return (address)this + round_to(sizeof(InterpreterCodelet), CodeEntryAlignment);
  }
  address code_end() const {
     return (address)this + size();
  }
  // ...
  int code_size() const {
     return code_end() - code_begin();
  }
  // ...
};

定义了3个属性及一些方法,其内存结构如下:在对齐至CodeEntryAlignment后,紧接着InterpreterCodelet的就是生成的目标代码。如下图所示。

 HotSpot的类模型(4)-LMLPHP

2、StubQueue类 

StubQueue是用来保存生成的本地代码的Stub队列,队列每一个元素对应一个InterpreterCodelet对象,InterpreterCodelet对象继承自抽象基类Stub,包含了字节码对应的本地代码以及一些调试和输出信息。

在TemplateInterpreter::initialize()方法中会创建StubQueue对象,如下: 

源代码位置:/src/share/vm/interpreter/templateInterpreter.cpp

void TemplateInterpreter::initialize() {
  if (_code != NULL)
       return;

  // 抽象解释器AbstractInterpreter的初始化,AbstractInterpreter是基于汇编模型的解释器的共同基类,
  // 定义了解释器和解释器生成器的抽象接口
  AbstractInterpreter::initialize();

  // 模板表TemplateTable的初始化,模板表TemplateTable保存了各个字节码的模板
  TemplateTable::initialize();

  // generate interpreter
  {
     ResourceMark rm;

     int code_size = InterpreterCodeSize;
     // CodeCache的Stub队列StubQueue的初始化
     _code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,"Interpreter");

     //  实例化模板解释器生成器对象TemplateInterpreterGenerator
     InterpreterGenerator g(_code);
  }

  // initialize dispatch table
  _active_table = _normal_table;
}

由于TemplateInterpreter继承自AbstractInterpreter,所以在TemplateInterpreter中初始化的_code属性其实就是AbstractInterpreter类中定义的_code属性:

StubQueue* _code   

StubQueue类的定义如下:

class StubQueue: public CHeapObj<mtCode> {
 private:
  StubInterface* _stub_interface;     // the interface prototype
  address        _stub_buffer;        // where all stubs are stored

  int            _buffer_size;       // the buffer size in bytes
  int            _buffer_limit;      // the (byte) index of the actual buffer limit (_buffer_limit <= _buffer_size)

  int            _queue_begin;       // the (byte) index of the first queue entry (word-aligned)
  int            _queue_end;         // the (byte) index of the first entry after the queue (word-aligned)

  int            _number_of_stubs;   // the number of buffered stubs

  Mutex* const   _mutex;             // the lock used for a (request, commit) transaction

  void check_index(int i) const {
	  assert(0 <= i && i < _buffer_limit && i % CodeEntryAlignment == 0, "illegal index");
  }
  bool is_contiguous() const {
	  return _queue_begin <= _queue_end;
  }
  int index_of(Stub* s) const {
	  int i = (address)s - _stub_buffer;
	  check_index(i);
	  return i;
  }
  Stub* stub_at(int i) const {
	  check_index(i);
	  return (Stub*)(_stub_buffer + i);
  }
  Stub* current_stub() const {
	  return stub_at(_queue_end);
  }

  // ...
}

这个类的构造函数如下:

StubQueue::StubQueue(
	 StubInterface* stub_interface,
	 int            buffer_size,
	 Mutex*         lock,
	 const char*    name) : _mutex(lock)
{
  intptr_t     size = round_to(buffer_size, 2*BytesPerWord);
  BufferBlob*  blob = BufferBlob::create(name, size); // 在StubQueue中创建BufferBlob

  _stub_interface  = stub_interface;

  _buffer_size     = blob->content_size();
  _buffer_limit    = blob->content_size();
  _stub_buffer     = blob->content_begin();

  _queue_begin     = 0;
  _queue_end       = 0;
  _number_of_stubs = 0;
  register_queue(this);
}

首先创建一个BufferBlob对象,然后对StubQueue中的属性进行初始化。调用的register_queue()方法的实现如下:

enum {  StubQueueLimit = 10  };  // there are only a few in the world
static StubQueue* registered_stub_queues[StubQueueLimit]; // 长度为10的StubQueue数组

void StubQueue::register_queue(StubQueue* sq) {
  for (int i = 0; i < StubQueueLimit; i++) {
     if (registered_stub_queues[i] == NULL) {
        registered_stub_queues[i] = sq;
        return;
     }
  }
  ShouldNotReachHere();
}

StubQueue如下:

HotSpot的类模型(4)-LMLPHP 

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

3、CodeletMark类

InterpreterCodelet依赖CodeletMark完成自动创始和初始化。CodeletMark继承自ResourceMark,允许自动析构,可对临时分配的代码缓存空间或汇编器内存空间自动回收。这个类的定义如下:

// A CodeletMark serves as an automatic creator/initializer for Codelets
// (As a subclass of ResourceMark it automatically GC's the allocated
// code buffer and assemblers).

class CodeletMark: ResourceMark {
 private:
  InterpreterCodelet*           _clet; // InterpreterCodelet继承自Stub
  InterpreterMacroAssembler**   _masm;
  CodeBuffer                    _cb;

 public:
  // 构造函数
  CodeletMark(
     InterpreterMacroAssembler*&    masm,
     const char*                    description,
     Bytecodes::Code                bytecode = Bytecodes::_illegal):
	  // AbstractInterpreter::code()获取的是StubQueue*类型的值,调用request()方法获取的
          // 是Stub*类型的值,调用的request()方法实现在vm/code/stubs.cpp文件中
	  _clet( (InterpreterCodelet*)AbstractInterpreter::code()->request(codelet_size()) ),
	  _cb(_clet->code_begin(), _clet->code_size())
  {

     // initialize Codelet attributes
     _clet->initialize(description, bytecode);

     // InterpreterMacroAssembler->MacroAssembler->Assembler->AbstractAssembler
     // 通过传入的cb.insts属性的值来初始化AbstractAssembler的_code_section与_oop_recorder属性的值
     // create assembler for code generation
     masm  = new InterpreterMacroAssembler(&_cb); // 在构造函数中,初始化r13指向bcp、r14指向本地局部变量表
     _masm = &masm;
  }

  // 析构函数
  ~CodeletMark() {
      // align so printing shows nop's instead of random code at the end (Codelets are aligned)
      (*_masm)->align(wordSize);

      // make sure all code is in code buffer
      (*_masm)->flush();

      // commit Codelet
      AbstractInterpreter::code()->commit((*_masm)->code()->pure_insts_size(), (*_masm)->code()->strings());

      // make sure nobody can use _masm outside a CodeletMark lifespan
      *_masm = NULL;
  }
};

在构造函数中主要完成2个任务:

(1)初始化InterpreterCodelet对象_clet。对InterpreterCodelet对象中的3个属性赋值。

(2)创建一个InterpreterMacroAssembler并赋值给masm与_masm,此对象会被用来生成代码。

通常在代码块结束时会自动调用析构函数,在析构函数中完成InterpreterCodelet的提交并清理相关变量的值。

在初始化_clet变量时,调用AbstractInterpreter::code()方法返回AbstractInterpreter类的_code属性的值,这个值在之前TemplateInterpreter::initialize()方法中已经初始化了。继续调用StubQueue类中的request()方法,传递的就是要求分配的用来存储code的大小,通过调用codelet_size()方法来获取,如下:

  int codelet_size() {
    // Request the whole code buffer (minus a little for alignment).
    // The commit call below trims it back for each codelet.
    int codelet_size = AbstractInterpreter::code()->available_space() - 2*K;

    return codelet_size;
  }

request()方法的实现如下:

Stub* StubQueue::request(int requested_code_size) {
  assert(requested_code_size > 0, "requested_code_size must be > 0");

  if (_mutex != NULL){
	  _mutex->lock();
  }

  Stub* s = current_stub();
  int requested_size = round_to(stub_code_size_to_size(requested_code_size), CodeEntryAlignment);
  if (requested_size <= available_space()) {
    if (is_contiguous()) {
      // Queue: |...|XXXXXXX|.............|
      //        ^0  ^begin  ^end          ^size = limit
      assert(_buffer_limit == _buffer_size, "buffer must be fully usable");
      if (_queue_end + requested_size <= _buffer_size) {
         // code fits in at the end => nothing to do
         CodeStrings strings;
         stub_initialize(s, requested_size, strings);
         return s; // 如果够的话就直接返回
      } else {
         // stub doesn't fit in at the queue end
         // => reduce buffer limit & wrap around
         assert(!is_empty(), "just checkin'");
         _buffer_limit = _queue_end;
         _queue_end = 0;
      }
    }
  }

  if (requested_size <= available_space()) {
    assert(!is_contiguous(), "just checkin'");
    assert(_buffer_limit <= _buffer_size, "queue invariant broken");
    // Queue: |XXX|.......|XXXXXXX|.......|
    //        ^0  ^end    ^begin  ^limit  ^size
    s = current_stub();
    CodeStrings strings;
    stub_initialize(s, requested_size, strings);
    return s;
  }

  // Not enough space left
  if (_mutex != NULL){
	  _mutex->unlock();
  }

  return NULL;
}

调用的stub_code_size_to_size()方法的实现如下:

// StubQueue类中的方法
int stub_code_size_to_size(int code_size) const {
	  return _stub_interface->code_size_to_size(code_size);
}
// InterpreterCodeletInterface类中的方法
virtual int  code_size_to_size(int code_size) const {
    	return InterpreterCodelet::code_size_to_size(code_size);
}
// InterpreterCodelet类中的方法
static  int code_size_to_size(int code_size) {
	  // CodeEntryAlignment = 32
	  // sizeof(InterpreterCodelet)  = 32
	  return round_to(sizeof(InterpreterCodelet), CodeEntryAlignment) + code_size;
}

通过如上的分配内存大小的方式可知内存结构如下:

HotSpot的类模型(4)-LMLPHP

调用的available_space()方法的实现如下:

// StubQueue类中定义的方法
int available_space() const {
	  int d = _queue_begin - _queue_end - 1;
	  return d < 0 ? d + _buffer_size : d;
}

is_contiguous()方法的实现如下:

  bool is_contiguous() const {
	  return _queue_begin <= _queue_end;
  }

调用的stub_initialize()方法的实现如下:

// 下面都是通过stubInterface来操作Stub的
// Stub functionality accessed via interface
// 在StubQueue类中定义
void  stub_initialize(Stub* s, int size,CodeStrings& strings)    {
	 assert(size % CodeEntryAlignment == 0, "size not aligned");
	 // 通过_stub_interface来操作Stub,会调用s的initialize()方法
	 _stub_interface->initialize(s, size, strings);
}

// 定义在InterpreterCodeletInterface类中
virtual void    initialize(Stub* self, int size,CodeStrings& strings){
    	cast(self)->initialize(size, strings);
}  

// 定义在InterpreterCodelet类中
void initialize(int size,CodeStrings& strings) {
	_size = size;
}

下面来看一下CodeletMark等类的在HotSpot中的具体使用。

在TemplateInterpreter::initialize()方法中初始化InterpreterGenerator对象时,调用的构造函数如下:

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

在TemplateInterpreterGenerator::generate_all()方法中的实现非常重要,这个方法生成了许多字节码指令以及一些虚拟机辅助执行的代码片段,如下:

{
    CodeletMark cm(_masm, "throw exception entrypoints");
    // ...
    Interpreter::_throw_NullPointerException_entry = generate_exception_handler("java/lang/NullPointerException",NULL);
    // ...
}

生成抛出空指针的代码片段。

address generate_exception_handler(const char* name, const char* message) {
    return generate_exception_handler_common(name, message, false);
}

调用的generate_exception_handler_common()方法的实现如下:

address TemplateInterpreterGenerator::generate_exception_handler_common(
        const char* name, const char* message, bool pass_oop) {
  assert(!pass_oop || message == NULL, "either oop or message but not both");
  address entry = __ pc();
  if (pass_oop) {
    // object is at TOS
    __ pop(c_rarg2);
  }
  // expression stack must be empty before entering the VM if an
  // exception happened
  __ empty_expression_stack();
  // setup parameters
  __ lea(c_rarg1, ExternalAddress((address)name));
  if (pass_oop) {
    __ call_VM(rax,
               CAST_FROM_FN_PTR(address,InterpreterRuntime::create_klass_exception),
               c_rarg1,c_rarg2);
  } else {
    // kind of lame ExternalAddress can't take NULL because
    // external_word_Relocation will assert.
    if (message != NULL) {
      __ lea(c_rarg2, ExternalAddress((address)message));
    } else {
      __ movptr(c_rarg2, NULL_WORD);
    }
    __ call_VM(rax,
               CAST_FROM_FN_PTR(address, InterpreterRuntime::create_exception),
               c_rarg1, c_rarg2);
  }
  // throw exception
  __ jump(ExternalAddress(Interpreter::throw_exception_entry()));


  address end =  __ pc();
  Disassembler::decode(entry, end);
  return entry;
}

生成的汇编代码如下:

  0x00007fffe10101cb: mov    -0x40(%rbp),%rsp
  0x00007fffe10101cf: movq   $0x0,-0x10(%rbp)
  0x00007fffe10101d7: movabs $0x7ffff6e09878,%rsi
  0x00007fffe10101e1: movabs $0x0,%rdx
  0x00007fffe10101eb: callq  0x00007fffe10101f5
  0x00007fffe10101f0: jmpq   0x00007fffe1010288
  0x00007fffe10101f5: lea    0x8(%rsp),%rax
  0x00007fffe10101fa: mov    %r13,-0x38(%rbp)
  0x00007fffe10101fe: mov    %r15,%rdi
  0x00007fffe1010201: mov    %rbp,0x200(%r15)
  0x00007fffe1010208: mov    %rax,0x1f0(%r15)
  0x00007fffe101020f: test   $0xf,%esp
  0x00007fffe1010215: je     0x00007fffe101022d
  0x00007fffe101021b: sub    $0x8,%rsp
  0x00007fffe101021f: callq  0x00007ffff66b3fbc
  0x00007fffe1010224: add    $0x8,%rsp
  0x00007fffe1010228: jmpq   0x00007fffe1010232
  0x00007fffe101022d: callq  0x00007ffff66b3fbc
  0x00007fffe1010232: movabs $0x0,%r10
  0x00007fffe101023c: mov    %r10,0x1f0(%r15)
  0x00007fffe1010243: movabs $0x0,%r10
  0x00007fffe101024d: mov    %r10,0x200(%r15)
  0x00007fffe1010254: cmpq   $0x0,0x8(%r15)
  0x00007fffe101025c: je     0x00007fffe1010267
  0x00007fffe1010262: jmpq   0x00007fffe1000420
  0x00007fffe1010267: mov    0x250(%r15),%rax
  0x00007fffe101026e: movabs $0x0,%r10
  0x00007fffe1010278: mov    %r10,0x250(%r15)
  0x00007fffe101027f: mov    -0x38(%rbp),%r13
  0x00007fffe1010283: mov    -0x30(%rbp),%r14
  0x00007fffe1010287: retq
  0x00007fffe1010288: jmpq   0x00007fffe100f3d3

在这里的重点不是读懂TemplateInterpreterGenerator::generate_exception_handler_common()方法的逻辑及生成的汇编代码,而是要清楚知道CodeletMark的应用,以及generate_exception_handler_common()方法生成的机器码是如何写入InterpreterCodelet中的。之前介绍过InterpreterCodelet与CodeBuffer类,如下:HotSpot的类模型(4)-LMLPHP 

通过CodeBuffer来操作InterpreterCodelet,而CodeBuffer中的代码部分(CodeSection)被赋值给AbstractAssembler::_code_section。

向CodeletMark中传入的_masm参数定义在AbstractInterpreterGenerator类中,如下:

class AbstractInterpreterGenerator: public StackObj {
   protected:
      InterpreterMacroAssembler* _masm;
      // ...
}

generate_exception_handler_common()方法中的__表示一个宏,定义如下:

#define __ _masm->

这样其实就是调用InterpreterMacroAssembler类中的相关方法写机器码,例如

__ pop(c_rarg2);

调用的pop()方法如下:

// 定义在InterpreterMacroAssembler中
void pop(Register r ) {
	  ((MacroAssembler*)this)->pop(r);
}

// 定义在Assembler类中
void Assembler::pop(Register dst) {
  int encode = prefix_and_encode(dst->encoding());
  emit_int8(0x58 | encode);
}

// 定义在AbstractAssembler类中
void emit_int8(   int8_t  x) {
   code_section()->emit_int8(   x);
}

code_section()方法获取的就是AbstractAssembler的_code_section属性的值。  

相关文章的链接如下:

1、在Ubuntu 16.04上编译OpenJDK8的源代码 

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程 

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)  

7、HotSpot的类模型(3) 

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)  

10、HotSpot的对象模型(6) 

11、操作句柄Handle(7)

12、句柄Handle的释放(8)

13、类加载器 

14、类的双亲委派机制 

15、核心类的预装载

16、Java主类的装载  

17、触发类的装载  

18、类文件介绍 

19、文件流 

20、解析Class文件 

21、常量池解析(1) 

22、常量池解析(2)

23、字段解析(1)

24、字段解析之伪共享(2) 

25、字段解析(3)  

26、字段解析之OopMapBlock(4)

27、方法解析之Method与ConstMethod介绍  

28、方法解析

29、klassVtable与klassItable类的介绍  

30、计算vtable的大小 

31、计算itable的大小 

32、解析Class文件之创建InstanceKlass对象 

33、字段解析之字段注入 

34、类的连接  

35、类的连接之验证 

36、类的连接之重写(1) 

37、类的连接之重写(2)

38、方法的连接  

39、初始化vtable 

40、初始化itable  

41、类的初始化 

42、对象的创建  

43、Java引用类型 

44、Java引用类型之软引用(1)

45、Java引用类型之软引用(2)

46、Java引用类型之弱引用与幻像引用  

47、Java引用类型之最终引用

48、HotSpot的垃圾回收算法  

49、HotSpot的垃圾回收器   

50、CallStub栈帧 

51、entry point栈帧  

52、generate_fixed_frame()方法生成Java方法栈帧 

53、dispatch_next()方法的实现  

54、虚拟机执行模式 

55、JVM的方法执行引擎-模板表 

作者持续维护的个人博客  classloading.com

关注公众号,有HotSpot源码剖析系列文章!

HotSpot的类模型(4)-LMLPHP   

 

08-21 20:04