1. 程式人生 > >JVM的方法執行引擎-模板表

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

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原始碼剖析系列文章!

   

  

&n