JVM的方法執行引擎-模板表

  • 2020 年 8 月 19 日
  • 筆記

Java的模板解析執行需要模板表與轉發表的支持,而這2個表中的數據在HotSpot虛擬機啟動時就會初始化。這一篇首先介紹模板表。

在啟動虛擬機階段會調用init_globals()方法初始化全局模塊,在這個方法中通過調用interpreter_init()方法初始化模板解釋器,調用棧如下:

TemplateInterpreter::initialize()    templateInterpreter.cpp
interpreter_init()                   interpreter.cpp
init_globals()                       init.cpp
Threads::create_vm()                 thread.cpp
JNI_CreateJavaVM()                   jni.cpp
InitializeJVM()                      java.c
JavaMain()                           java.c
start_thread()                       pthread_create.c   

interpreter_init()方法主要是通過調用TemplateInterpreter::initialize()方法來完成邏輯,initialize()方法的實現如下:

源代碼位置:/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;
}

模板解釋器的初始化包括如下幾個方面:

(1)抽象解釋器AbstractInterpreter的初始化,AbstractInterpreter是基於彙編模型的解釋器的共同基類,定義了解釋器和解釋器生成器的抽象接口。

(2)模板表TemplateTable的初始化,模板表TemplateTable保存了各個位元組碼的模板(目標代碼生成函數和參數);

(3)CodeCache的Stub隊列StubQueue的初始化;

(4)解釋器生成器InterpreterGenerator的初始化。

在之前介紹過,在TemplateInterpreter::initialize() 中通過調用語句來間接調用generate_method_entry()和generate_normal_entry()創建方法執行的棧幀:

InterpreterGenerator g(_code);

不過在如上語句調用之前,首先需要調用TemplateInterpreter類中的initialize()方法初始化模板表,如下:

TemplateTable::initialize();

模板表TemplateTable保存了各個位元組碼的模板(目標代碼生成函數和參數),initialize()方法的實現如下:

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

void TemplateTable::initialize() {
  if (_is_initialized) return;


  _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::_tableswitch         , ubcp|disp|____|____, itos, vtos, tableswitch         ,  _           );
  def(Bytecodes::_lookupswitch        , ubcp|disp|____|____, itos, itos, lookupswitch        ,  _           );
  def(Bytecodes::_ireturn             , ____|disp|clvm|____, itos, itos, _return             , itos         );
  def(Bytecodes::_lreturn             , ____|disp|clvm|____, ltos, ltos, _return             , ltos         );
  def(Bytecodes::_freturn             , ____|disp|clvm|____, ftos, ftos, _return             , ftos         );
  def(Bytecodes::_dreturn             , ____|disp|clvm|____, dtos, dtos, _return             , dtos         );
  def(Bytecodes::_areturn             , ____|disp|clvm|____, atos, atos, _return             , atos         );
  def(Bytecodes::_return              , ____|disp|clvm|____, vtos, vtos, _return             , vtos         );
  def(Bytecodes::_getstatic           , ubcp|____|clvm|____, vtos, vtos, getstatic           , f1_byte      );
  def(Bytecodes::_putstatic           , ubcp|____|clvm|____, vtos, vtos, putstatic           , f2_byte      );
  def(Bytecodes::_getfield            , ubcp|____|clvm|____, vtos, vtos, getfield            , f1_byte      );
  def(Bytecodes::_putfield            , ubcp|____|clvm|____, vtos, vtos, putfield            , f2_byte      );
  def(Bytecodes::_invokevirtual       , ubcp|disp|clvm|____, vtos, vtos, invokevirtual       , f2_byte      );
  def(Bytecodes::_invokespecial       , ubcp|disp|clvm|____, vtos, vtos, invokespecial       , f1_byte      );
  def(Bytecodes::_invokestatic        , ubcp|disp|clvm|____, vtos, vtos, invokestatic        , f1_byte      );
  def(Bytecodes::_invokeinterface     , ubcp|disp|clvm|____, vtos, vtos, invokeinterface     , f1_byte      );
  def(Bytecodes::_invokedynamic       , ubcp|disp|clvm|____, vtos, vtos, invokedynamic       , f1_byte      );
  def(Bytecodes::_new                 , ubcp|____|clvm|____, vtos, atos, _new                ,  _           );
  def(Bytecodes::_newarray            , ubcp|____|clvm|____, itos, atos, newarray            ,  _           );
  def(Bytecodes::_anewarray           , ubcp|____|clvm|____, itos, atos, anewarray           ,  _           );
  def(Bytecodes::_arraylength         , ____|____|____|____, atos, itos, arraylength         ,  _           );
  def(Bytecodes::_athrow              , ____|disp|____|____, atos, vtos, athrow              ,  _           );
  def(Bytecodes::_checkcast           , ubcp|____|clvm|____, atos, atos, checkcast           ,  _           );
  def(Bytecodes::_instanceof          , ubcp|____|clvm|____, atos, itos, instanceof          ,  _           );
  def(Bytecodes::_monitorenter        , ____|disp|clvm|____, atos, vtos, monitorenter        ,  _           );
  def(Bytecodes::_monitorexit         , ____|____|clvm|____, atos, vtos, monitorexit         ,  _           );
  def(Bytecodes::_wide                , ubcp|disp|____|____, vtos, vtos, wide                ,  _           );
  def(Bytecodes::_multianewarray      , ubcp|____|clvm|____, vtos, atos, multianewarray      ,  _           );
  def(Bytecodes::_ifnull              , ubcp|____|clvm|____, atos, vtos, if_nullcmp          , equal        );
  def(Bytecodes::_ifnonnull           , ubcp|____|clvm|____, atos, vtos, if_nullcmp          , not_equal    );
  def(Bytecodes::_goto_w              , ubcp|____|clvm|____, vtos, vtos, goto_w              ,  _           );
  def(Bytecodes::_jsr_w               , ubcp|____|____|____, vtos, vtos, jsr_w               ,  _           );

  // wide Java spec bytecodes
  def(Bytecodes::_iload               , ubcp|____|____|iswd, vtos, itos, wide_iload          ,  _           );
  def(Bytecodes::_lload               , ubcp|____|____|iswd, vtos, ltos, wide_lload          ,  _           );
  // ...

  // 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        );
  def(Bytecodes::_fast_fgetfield      , ubcp|____|____|____, atos, ftos, fast_accessfield    ,  ftos        );
  def(Bytecodes::_fast_igetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );
  def(Bytecodes::_fast_lgetfield      , ubcp|____|____|____, atos, ltos, fast_accessfield    ,  ltos        );
  def(Bytecodes::_fast_sgetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );

  def(Bytecodes::_fast_aputfield      , ubcp|____|____|____, atos, vtos, fast_storefield ,   atos        );
  def(Bytecodes::_fast_bputfield      , ubcp|____|____|____, itos, vtos, fast_storefield ,   itos        );
  def(Bytecodes::_fast_cputfield      , ubcp|____|____|____, itos, vtos, fast_storefield  ,  itos        );
  def(Bytecodes::_fast_dputfield      , ubcp|____|____|____, dtos, vtos, fast_storefield  ,  dtos        );
  def(Bytecodes::_fast_fputfield      , ubcp|____|____|____, ftos, vtos, fast_storefield  ,  ftos        );
  def(Bytecodes::_fast_iputfield      , ubcp|____|____|____, itos, vtos, fast_storefield  ,  itos        );
  def(Bytecodes::_fast_lputfield      , ubcp|____|____|____, ltos, vtos, fast_storefield  ,  ltos        );
  def(Bytecodes::_fast_sputfield      , ubcp|____|____|____, itos, vtos, fast_storefield  ,  itos        );

  def(Bytecodes::_fast_aload_0        , ____|____|____|____, vtos, atos, aload               ,  0           );
  def(Bytecodes::_fast_iaccess_0      , ubcp|____|____|____, vtos, itos, fast_xaccess        ,  itos        );
  def(Bytecodes::_fast_aaccess_0      , ubcp|____|____|____, vtos, atos, fast_xaccess        ,  atos        );
  def(Bytecodes::_fast_faccess_0      , ubcp|____|____|____, vtos, ftos, fast_xaccess        ,  ftos        );

  def(Bytecodes::_fast_iload          , ubcp|____|____|____, vtos, itos, fast_iload          ,  _       );
  def(Bytecodes::_fast_iload2         , ubcp|____|____|____, vtos, itos, fast_iload2         ,  _       );
  def(Bytecodes::_fast_icaload        , ubcp|____|____|____, vtos, itos, fast_icaload        ,  _       );

  def(Bytecodes::_fast_invokevfinal   , ubcp|disp|clvm|____, vtos, vtos, fast_invokevfinal   , f2_byte      );

  def(Bytecodes::_fast_linearswitch   , ubcp|disp|____|____, itos, vtos, fast_linearswitch   ,  _           );
  def(Bytecodes::_fast_binaryswitch   , ubcp|disp|____|____, itos, vtos, fast_binaryswitch   ,  _           );

  def(Bytecodes::_fast_aldc           , ubcp|____|clvm|____, vtos, atos, fast_aldc           ,  false       );
  def(Bytecodes::_fast_aldc_w         , ubcp|____|clvm|____, vtos, atos, fast_aldc           ,  true        );

  def(Bytecodes::_return_register_finalizer , ____|disp|clvm|____, vtos, vtos, _return       ,  vtos        );

  def(Bytecodes::_invokehandle        , ubcp|disp|clvm|____, vtos, vtos, invokehandle        , f1_byte      );

  def(Bytecodes::_shouldnotreachhere   , ____|____|____|____, vtos, vtos, shouldnotreachhere ,  _           );
  // platform specific bytecodes
  pd_initialize();

  _is_initialized = true;
}

TemplateTable的初始化調用def()將所有位元組碼的目標代碼生成函數和參數保存在_template_table或_template_table_wide(wide指令)模板數組中。除了虛擬機規範本身定義的位元組碼指令外,HotSpot虛擬機也定義了一些位元組碼指令,這些指令為了輔助虛擬機進行更好、理簡單的功能實現,例如Bytecodes::_return_register_finalizer等在之前已經介紹過,可以更好的實現finalizer類型對象的註冊功能。

對於調用def()函數時傳遞的一些參數在後面會解釋。def()函數有2個,接收的參數不同,實現如下:

void TemplateTable::def(
        Bytecodes::Code code, // 位元組碼指令
	int flags,            // 標誌位
	TosState in,          // 模板執行前TosState
	TosState out,         // 模板執行後TosState
	void (*gen)(),        // 模板生成器,是模板的核心組件
	char filler
) {
  assert(filler == ' ', "just checkin'");
  def(code, flags, in, out, (Template::generator)gen, 0); // 調用下面的def()函數
}

void TemplateTable::def(
  Bytecodes::Code code,    // 位元組碼指令
  int flags,               // 標誌位
  TosState in,             // 模板執行前TosState
  TosState out,            // 模板執行後TosState
  void (*gen)(int arg),    // 模板生成器,是模板的核心組件
  int arg 
) {
  // should factor out these constants
  const int ubcp = 1 << Template::uses_bcp_bit;      // 表示是否需要bcp指針
  const int disp = 1 << Template::does_dispatch_bit; // 表示是否在模板範圍內進行轉發
  const int clvm = 1 << Template::calls_vm_bit;      // 表示是否需要調用JVM函數
  const int iswd = 1 << Template::wide_bit;          // 表示是否為wild指令

  // 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); // 調用模板表t的initialize()方法初始化模板表
  assert(t->bytecode() == code, "just checkin'");
}

模板表由模板表數組與一組生成器組成:

(1)模板表數組有_template_table與_template_table_wild,數組的下標為bytecode,值為Template,按照位元組碼指令的操作碼遞增順序排列。

(2)一組生成器,所有與bytecode配套的生成器,在初始化模板表時作為gen參數傳給相應的Template。

Template類的定義如下:

源代碼位置:hotspot/src/share/vm/interpreter/templateTable.hpp
// A Template describes the properties of a code template for a given bytecode
// and provides a generator to generate the code template.

class Template VALUE_OBJ_CLASS_SPEC {
 private:
  enum Flags {
    // 位元組碼指令指的是該位元組碼的操作數是否存在於位元組碼裏面
    uses_bcp_bit,     // set if template needs the bcp pointing to bytecode
    does_dispatch_bit,// set if template dispatches on its own
    calls_vm_bit,     // set if template calls the vm
    wide_bit          // set if template belongs to a wide instruction
  };

  typedef void (*generator)(int arg);

  int       _flags;   // describes interpreter template properties (bcp unknown)
  TosState  _tos_in;  // tos cache state before template execution
  TosState  _tos_out; // tos cache state after  template execution
  generator _gen;     // template code generator
  int       _arg;     // argument for template code generator
  ...

  // Templates
  static Template* template_for(Bytecodes::Code code)  {
	  Bytecodes::check(code);
	  return &_template_table[code];
  }
  static Template* template_for_wide(Bytecodes::Code code)  {
	  Bytecodes::wide_check(code);
	  return &_template_table_wide[code];
  }
};

調用的template_for()與template_for_wild()方法從_template_table或_template_for_wild數組中取值。這2個變量定義在TemplateTable類中,如下:

static Template        _template_table     [Bytecodes::number_of_codes];
static Template        _template_table_wide[Bytecodes::number_of_codes];

繼續看TemplateTable::def(0函數的各個參數,解釋如下:

(1)_flags:是一個標誌,低四位分別表示:

  • uses_bcp_bit,標誌需要使用位元組碼指針(byte code pointer,數值為位元組碼基址+位元組碼偏移量)
  • does_dispatch_bit,標誌是否在模板範圍內進行轉發,如跳轉類指令會設置該位
  • calls_vm_bit,標誌是否需要調用JVM函數
  • wide_bit,標誌是否是wide指令(使用附加位元組擴展全局變量索引)

(2)_tos_in:表示模板執行前的TosState(操作數棧棧頂元素的數據類型,TopOfStack,用來檢查模板所聲明的輸出輸入類型是否和該函數一致,以確保棧頂元素被正確使用)

(3)_tos_out:表示模板執行後的TosState 

(4)_gen:表示模板生成器(函數指針)

(5)_arg:表示模板生成器參數

再來看一下TemplateTable::initialize()方法中對def()函數的調用,以_iinc(將局部變量增加1)為例,調用如下:

def(
	Bytecodes::_iinc,     // 位元組碼指令
	ubcp|____|clvm|____,  // 標誌
	vtos,                 // 模板執行前的TosState
	vtos,                 // 模板執行後的TosState
	iinc ,                // 模板生成器,是一個iinc()函數的指針
	_                     // 不需要模板生成器參數
); 

設置標誌位uses_bcp_bit和calls_vm_bit,表示iinc指令的生成器需要使用bcp指針函數at_bcp(),且需要調用JVM函數,下面給出了生成器的定義:

源代碼位置:/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp

void TemplateTable::iinc() {
  transition(vtos, vtos);
  __ load_signed_byte(rdx, at_bcp(2)); // get constant
  locals_index(rbx);
  __ addl(iaddress(rbx), rdx);
}

iinc指令的格式如下:

iinc
index
const

操作碼iinc佔用一個位元組,而index與const分別佔用一個位元組。使用at_bcp()函數獲取iinc指令的操作數,2表示偏移2位元組,所以會將const取出來存儲到rdx中。調用locals_index()函數取出index,locals_index()就是JVM函數。最終生成的彙編如下:

// %r13存儲的是指向位元組碼的指針,偏移2位元組後取出const存儲到%edx
0x00007fffe101a210: movsbl 0x2(%r13),%edx
// 取出index存儲到%ebx
0x00007fffe101a215: movzbl 0x1(%r13),%ebx
0x00007fffe101a21a: neg    %rbx
// %r14指向本地變量表的首地址,將%edx加到%r14+%rbx*8指向的內存所存儲的值上
// 之所以要對%rbx執行neg進行符號反轉,是因為在Linux內核的操作系統上,棧是向低地址方向生長的
0x00007fffe101a21d: add    %edx,(%r14,%rbx,8)

不過這裡並不會調用iinc()函數生成對應的彙編代碼,只是將傳遞給def()函數的各種信息保存到Template對象中,在TemplateTable::def()方法中,通過template_for()或template_for_wild()方法獲取到數組中對應的Template對象後,就會調用Template::initialize()方法,實現如下:

void Template::initialize(int flags, TosState tos_in, TosState tos_out, generator gen, int arg) {
  _flags   = flags;
  _tos_in  = tos_in;
  _tos_out = tos_out;
  _gen     = gen;
  _arg     = arg;
}

可以看到,只是將信息保存到對應的Template對象中,這樣就可以根據位元組碼索引從數組中獲取對應的Template對象,進而獲取相關信息。下一篇我們將會看到對這些信息的使用。  

相關文章的鏈接如下:

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、虛擬機執行模式 

作者持續維護的個人博客  classloading.com

關注公眾號,有HotSpot源碼剖析系列文章!