klassVtable與klassItable類用來實現Java方法的多態,也可以叫動態綁定,是指在應用執行期間通過判斷接受對象的實際類型,根據實際類型調用對應的方法。C++為了實現多態,在對象中嵌入了虛函數表vtable,通過虛函數表來實現運行期的方法分派,這在之前介紹HotSpot的二分模型時簡單介紹過,這里不再介紹C++的方法分派。
1、klassVtable類
C++中的vtable只包含虛函數,非虛函數在編譯期就已經解析出正確的方法調用了。Java vtable除了虛方法外還包含了其他的非虛方法。
vtable中的一條記錄用vtableEntry表示,該類在klassVtable.hpp文件中定義,只定義了一個屬性Method* _method,所以說此類只是對Method*做了簡單包裝而已,提供了操作Method對象的方法。
訪問vtable需要通過klassVtable類,該類在klassVtable.hpp文件中定義,提供了操作vtable的方法,如Method* method_at(int i)、int index_of(Method* m)等,其實現都是基於vtable的內存起始地址和內存偏移完成的。
klassVtable類的定義及屬性的聲明如下:
A klassVtable abstracts the variable-length(可變長度) vtable that is embedded in InstanceKlass and ArrayKlass. klassVtable objects are used just as convenient transient accessors to the vtable,not to actually hold the vtable data. Note: the klassVtable should not be accessed before the class has been verified(until that point, the vtable is uninitialized). Currently a klassVtable contains a direct reference to the vtable data, and is therefore not preserved across GCs. class klassVtable : public ResourceObj { KlassHandle _klass; // my klass int _tableOffset; // offset of start of vtable data within klass int _length; // length of vtable (number of entries) ... }
屬性的介紹如下:
(1)_klass:該vtable所屬的klass
(2)_tableOffset:vtable在klass實例內存中的偏移量
(3)_length:vtable的長度,即vtableEntry的數量,因為一個vtableEntry實例只包含一個Method*,其大小等於字寬(一個指針的寬度),所以vtable的長度跟vtable以字寬為單位的內存大小相同
下面介紹一下vtableEntry類。
vtableEntry類的定義及屬性的聲明如下:
class vtableEntry VALUE_OBJ_CLASS_SPEC { ... private: Method* _method; ... };
這個類只是對_method進行了簡單的封裝。
vtable表示是由一組變長(前面會有一個字段描述該表的長度)連續的vtableEntry元素構成的數組。其中每個vtableEntry封裝了一個Method對象。在類初始化時,HotSpot將復制父類的vtable,然后根據自己定義的方法更新vtableEntry,或向vtable中添加新的vtableEntry對象。當Java方法重寫父類方法時,HotSpot將更新vtable中表示被重寫方法的vtableEntry,使其指向覆蓋后的實現方法;如果是方法重載或者自身新增的方法,HotSpot將按順序添加到vtable中。尚未提供實現的Java方法也放在了vtable中,因為沒有實現,HotSpot沒有為這個vtableEntry項分發具體的方法,這和C++的純虛函數類似,不再贅述。調用類方法時,HotSpot通過ConstantPoolCacheEntry的_f2成員獲取vtable中方法的索引,從而取到Method對象以便執行。關於ConstantPoolCacheEntry類及相關屬性在后面會詳細介紹。
2、klassItable
Java itable是Java接口函數表,為了方便查找某個接口對應的方法實現。itable的結構比vtable復雜,除了記錄方法地址外還得記錄該方法所屬的接口類klass。
itable表由偏移表和方法表兩個表組成,這兩個表都是變長的。每個offset table entry保存的是類實現的一個接口klassOop和該接口方法表所在的偏移位置;方法表method table entry元素保存的是實現的接口方法,方法在方法表的位置同樣是使用ConstantPoolCacheEntry的_f2成員保存的。在初始化itable時,HotSpot將類實現的接口以及實現的方法填寫在上述兩張表中。接口中的非public方法和abstract方法(在vtable中占一個槽位)不放入itable中。調用接口方法時,HotSpot通過ConstantPoolCacheEntry的_f1成員拿到接口的klassOop,在itable的偏移表中逐一匹配,如果匹配上則獲取它的方法表的位置,然后在方法表中通過ConstantPoolCacheEntry的_f2成員找到實現的方法Method。
類及屬性的定義如下:
class klassItable : public ResourceObj { private: instanceKlassHandle _klass; // my klass int _table_offset; // offset of start of itable data within klass (in words) int _size_offset_table; // size of offset table (in itableOffset entries) int _size_method_table; // size of methodtable (in itableMethodEntry entries) ... }
該類包含4個屬性:
(1)_klass:itable所屬的Klass
(2)_table_offset:itable在所屬Klass中的內存偏移量
(3)_size_offset_table:itable中itableOffsetEntry的數量
(4)_size_method_table:itable中itableMethodEntry的數量
方法所屬的接口類klass地址用itableOffsetEntry表示,類的定義如下:
class itableOffsetEntry VALUE_OBJ_CLASS_SPEC { private: Klass* _interface; int _offset; ... }
包含兩個屬性,如下:
(1) _interface:該方法所屬的接口
(2)_offset:該接口下的第一個方法itableMethodEntry相對於所屬Klass的偏移量
方法地址用itableMethodEntry表示,定義如下:
class itableMethodEntry VALUE_OBJ_CLASS_SPEC { private: Method* _method; ... }
跟vtableEntry一樣,只包含了一個_method屬性。
為什么需要itable,而不是用vtable解決所有問題。
一個類可以實現多個接口,而每個接口的函數編號是和自己相關的,vtable 無法解決多個對應接口的函數編號問題。而一個子類只能繼承一個父親,子類只要包含父類vtable,並且和父類的函數包含部分編號是一致的,就可以直接使用父類的函數編號找到對應的子類實現函數。
相關文章的鏈接如下:
1、在Ubuntu 16.04上編譯OpenJDK8的源代碼
13、類加載器
14、類的雙親委派機制
15、核心類的預裝載
16、Java主類的裝載
17、觸發類的裝載
18、類文件介紹
19、文件流
20、解析Class文件
21、常量池解析(1)
22、常量池解析(2)
23、字段解析(1)
24、字段解析之偽共享(2)
25、字段解析(3)
28、方法解析
作者持續維護的個人博客classloading.com。
關注公眾號,有HotSpot源碼剖析系列文章!
參考文章:
(2)JVM Anatomy Park #16: 超多態虛調用 https://www.jianshu.com/p/704fce44840f
(3)The Black Magic of (Java) Method Dispatch https://shipilev.net/blog/2015/black-magic-method-dispatch/
(4)https://www.zhihu.com/question/34846173?sort=created
(5)https://www.zhihu.com/question/56936880/answer/152203730
(6)https://hllvm-group.iteye.com/group/topic/29140