第15章-解釋器及解釋器生成器


方法解釋執行時需要解釋器與解釋器生成器的支持。解釋器與解釋器生成器的繼承體系如下:

下面詳細介紹解釋器與解釋器生成器。

1、解釋器

解釋器是一堆本地代碼例程構造的,這些例程會在虛擬機啟動的時候寫入到StubQueue中,以后解釋執行時就只需要進入指定例程即可。

解釋器的繼承體系如下:

AbstractInterpreter        /interpreter/abstractInterpreter.hpp
     CppInterpreter
     TemplateInterpreter   /interpreter/templateInterpreter.hpp
            Interpreter    /interpreter/templateInterpreter.hpp

Interpreter通過宏可以繼承自CppInterpreter或者TemplateInterpreter,前者稱為C++解釋器,每個字節碼指令都對應一段C++代碼,通過switch的方式處理字節碼,后者稱為模板解釋器,每個指令對應一段機器指令片段,通過指令模板的方式處理字節碼,HotSpot VM默認使用模板解釋器。

(1)抽象解釋器AbstractInterpreter

所有的解釋器都繼承自抽象解釋器,類及重要屬性的定義如下:

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

_code屬性在之前已經介紹過,這是一個隊列,隊列中的InterpreterCodelet表示一個例程,比如iconst_1對應的代碼,invokedynamic對應的代碼,異常處理對應的代碼,方法入口點對應的代碼,這些代碼都是一個個InterpreterCodelet。整個解釋器都是由這些例程組成的,每個例程完成解釋器的部分功能,以此實現整個解釋器。

_entry_table數組會會保存方法入口點,例如普通方法的入口點為_entry_table[0]、同步的普通方法的入口點為_entry_table[1],這些_entry_table[0],_entry_table[1]指向的就是之前_code隊列里面的例程。這些邏輯都在是generate_all()函數中完成的,如下:  

void TemplateInterpreterGenerator::generate_all() {
  // ...

  method_entry(zerolocals)
  method_entry(zerolocals_synchronized)
  method_entry(empty)
  method_entry(accessor)
  method_entry(abstract)
  method_entry(java_lang_math_sin  )
  method_entry(java_lang_math_cos  )
  method_entry(java_lang_math_tan  )
  method_entry(java_lang_math_abs  )
  method_entry(java_lang_math_sqrt )
  method_entry(java_lang_math_log  )
  method_entry(java_lang_math_log10)
  method_entry(java_lang_math_exp  )
  method_entry(java_lang_math_pow  )
  method_entry(java_lang_ref_reference_get)
  
  // ...
}

method_entry宏的定義如下:

#define method_entry(kind)                                                                    \
{                                                                                             \
    CodeletMark cm(_masm, "method entry point (kind = " #kind ")");                           \
    Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind);  \
}

可以看到,調用generate_method_entry()函數會返回例程對應的入口地址,然后保存到AbstractInterpreter類中定義的_entry_table數組中。調用generate_method_entry()函數傳入的參數是枚舉常量,表示一些特殊的方法和一些常見的方法類型。

(2)模板解釋器TemplateInterpreter

模板解釋器類的定義如下:

class TemplateInterpreter: public AbstractInterpreter {
  protected:
    // 數組越界異常例程
    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)解釋器Interpreter

類的定義如下:

class Interpreter: public CC_INTERP_ONLY(CppInterpreter) NOT_CC_INTERP(TemplateInterpreter) {
  // ...
}

沒有定義新的屬性,只有幾個函數。Interpreter默認通過宏擴展的方式繼承TemplateInterpreter。 

2、解釋器生成器

要想得到可運行的解釋器還需要解釋器生成器。解釋器生成器本來可以獨自完成填充工作,可能為了解耦,也可能是為了結構清晰,HotSpot VM將字節碼的例程抽了出來放到了TemplateTable模板表中,它輔助模板解釋器生成器templateInterpreterGenerator生成各種例程。

解釋器生成器的繼承體系如下:

AbstractInterpreterGenerator        /interpreter/abstractInterpreter.hpp
     TemplateInterpreterGenerator   /interpreter/templateInterpreter.hpp
              InterpreterGenerator  /interpreter/interpreter.hpp

模板解釋器生成器擴展了抽象解釋器生成器。解釋器生成器與解釋器其實有某種意義上的對應關系,如抽象解釋器生成器中定義了一些函數,調用這些函數會初始化抽象解釋器中的屬性,如保存例程的_entry_table數組等,在模板解釋器生成器中定義的函數會初始化模板解釋器中定義的一些屬性,如_throw_ArrayIndexOutOfBoundsException_entry等。之前介紹過空指針的例程就是在這個TemplateInterpreterGenerator類的generate_all()函數中生成的。如下:

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

關於解釋器生成器不再過多介紹。

這里我們需要提醒的是,解釋器和解釋器生成器中定義的函數在實現過程中,有些和平台是無關的,所以會在/interpreter文件夾下的文件中實現。例如Interpreter和InterpreterGenerator類定義在/interpreter文件夾下,其中定義的函數會在/interpreter文件夾下的interpreter.cpp文件中實現,但是有些函數是針對特定平台,我們只討論linux在x86架構下的64位實現,所以cpu/x86/vm文件夾下也有interpreter_x86.hpp和interpreter_x86_64.cpp等文件,只需要在定義Interpreter類時包含interpreter_x86.hpp文件即可。 

推薦閱讀:

第1篇-關於JVM運行時,開篇說的簡單些

第2篇-JVM虛擬機這樣來調用Java主類的main()方法

第3篇-CallStub新棧幀的創建

第4篇-JVM終於開始調用Java主類的main()方法啦

第5篇-調用Java方法后彈出棧幀及處理返回結果

第6篇-Java方法新棧幀的創建

第7篇-為Java方法創建棧幀

第8篇-dispatch_next()函數分派字節碼

第9篇-字節碼指令的定義

第10篇-初始化模板表

第11篇-認識Stub與StubQueue

第12篇-認識CodeletMark

第13篇-通過InterpreterCodelet存儲機器指令片段

第14篇-生成重要的例程

如果有問題可直接評論留言或加作者微信mazhimazh

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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM