HotSpot的類模型(3)
上一篇 HotSpot的類模型(2) 介紹了類模型的基礎類Klass的重要屬性及方法,這一篇介紹一下InstanceKlass及InstanceKlass的子類。
2、InstanceKlass類
每個InstanceKlass物件表示一個具體的Java類(這裡的Java類不包括Java陣列)。InstanceKlass類及重要屬性的定義如下:
class InstanceKlass: public Klass { ... protected: // Annotations for this class Annotations* _annotations; // Array classes holding elements of this class. Klass* _array_klasses; // Constant pool for this class. ConstantPool* _constants; // The InnerClasses attribute and EnclosingMethod attribute. The // _inner_classes is an array of shorts. If the class has InnerClasses // attribute, then the _inner_classes array begins with 4-tuples of shorts // [inner_class_info_index, outer_class_info_index, // inner_name_index, inner_class_access_flags] for the InnerClasses // attribute. If the EnclosingMethod attribute exists, it occupies the // last two shorts [class_index, method_index] of the array. If only // the InnerClasses attribute exists, the _inner_classes array length is // number_of_inner_classes * 4. If the class has both InnerClasses // and EnclosingMethod attributes the _inner_classes array length is // number_of_inner_classes * 4 + enclosing_method_attribute_size. Array<jushort>* _inner_classes; // Array name derived from this class which needs unreferencing // if this class is unloaded. Symbol* _array_name; // Number of heapOopSize words used by non-static fields in this klass // (including inherited fields but after header_size()). int _nonstatic_field_size; int _static_field_size; // number words used by static fields (oop and non-oop) in this klass // Constant pool index to the utf8 entry of the Generic signature, // or 0 if none. u2 _generic_signature_index; // Constant pool index to the utf8 entry for the name of source file // containing this klass, 0 if not specified. u2 _source_file_name_index; u2 _static_oop_field_count;// number of static oop fields in this klass u2 _java_fields_count; // The number of declared Java fields int _nonstatic_oop_map_size;// size in words of nonstatic oop map blocks u2 _minor_version; // minor version number of class file u2 _major_version; // major version number of class file Thread* _init_thread; // Pointer to current thread doing initialization (to handle recusive initialization) int _vtable_len; // length of Java vtable (in words) int _itable_len; // length of Java itable (in words) OopMapCache* volatile _oop_map_cache; // OopMapCache for all methods in the klass (allocated lazily) JNIid* _jni_ids; // First JNI identifier for static fields in this class jmethodID* _methods_jmethod_ids; // jmethodIDs corresponding to method_idnum, or NULL if none nmethodBucket* _dependencies; // list of dependent nmethods nmethod* _osr_nmethods_head; // Head of list of on-stack replacement nmethods for this class // Class states are defined as ClassState (see above). // Place the _init_state here to utilize the unused 2-byte after // _idnum_allocated_count. u1 _init_state; // state of class u1 _reference_type; // reference type // Method array. Array<Method*>* _methods; // Default Method Array, concrete methods inherited from interfaces Array<Method*>* _default_methods; // Interface (Klass*s) this class declares locally to implement. Array<Klass*>* _local_interfaces; // Interface (Klass*s) this class implements transitively. Array<Klass*>* _transitive_interfaces; // Int array containing the vtable_indices for default_methods // offset matches _default_methods offset Array<int>* _default_vtable_indices; // Instance and static variable information, starts with 6-tuples of shorts // [access, name index, sig index, initval index, low_offset, high_offset] // for all fields, followed by the generic signature data at the end of // the array. Only fields with generic signature attributes have the generic // signature data set in the array. The fields array looks like following: // // f1: [access, name index, sig index, initial value index, low_offset, high_offset] // f2: [access, name index, sig index, initial value index, low_offset, high_offset] // ... // fn: [access, name index, sig index, initial value index, low_offset, high_offset] // [generic signature index] // [generic signature index] // ... Array<u2>* _fields; // embedded Java vtable follows here // embedded Java itables follows here // embedded static fields follows here // embedded nonstatic oop-map blocks follows here // embedded implementor of this interface follows here // The embedded implementor only exists if the current klass is an // iterface. The possible values of the implementor fall into following // three cases: // NULL: no implementor. // A Klass* that's not itself: one implementor. // Itsef: more than one implementors. // embedded host klass follows here // The embedded host klass only exists in an anonymous class for // dynamic language support (JSR 292 enabled). The host class grants // its access privileges to this class also. The host class is either // named, or a previously loaded anonymous class. A non-anonymous class // or an anonymous class loaded through normal classloading does not // have this embedded field. ... }
重要屬性的介紹如下表所示。
欄位名 | 作用 |
_annotations | Annotations型別的指標,儲存該類使用的所有註解 |
_array_klasses |
陣列元素為該類的陣列Klass指標,例如ObjArrayKlass是物件陣列且元素型別為Object, 那麼表示Object類的InstanceKlass物件的_array_klasses就是指向ObjArrayKlass的指標 |
_array_name |
以該類為陣列元素的陣列的名字,如"[Ljava/lang/Object;" |
_constants | ConstantPool型別的指標,用來儲存類的常量池資訊 |
_inner_classes | 用一個jushort陣列儲存當前類的InnerClasses屬性和EnclosingMethod屬性 |
_nonstatic_field_size | 非靜態欄位需要佔用的記憶體大小 ,以字為單位 |
_static_field_size | 靜態欄位需要佔用的記憶體大小 ,以字為單位 |
_generic_signature_index |
儲存此類的Generic signature在常量池中的索引 |
_source_file_name_index | 儲存此類的原始檔名在常量池中索引 |
_static_oop_field_count | 此類包含的靜態引用型別欄位的數量 |
_java_fields_count | 欄位總數量 |
_nonstatic_oop_map_size | 非靜態的oop map block的記憶體大小,以字為單位 |
_minor_version | 類的次版本號 |
_major_version | 類的主版本號 |
_init_thread | 執行此類初始化的Thread指標 |
_vtable_len | Java虛擬函式表(vtable)所佔用的記憶體大小,以字為單位 |
_itable_len | Java介面函式表(itable)所佔用的記憶體大小,以字為單位 |
_oop_map_cache | OopMapCache指標,該類的所有方法的OopMapCache |
_jni_ids/_methods_jmethod_ids | JNIid指標與jmethodID指標,這2個指標對於JNI方法操作屬性和方法非常重要,在介紹JNI時會詳細介紹。 |
_dependencies | nmethodBucket指標,依賴的本地方法,以根據其_next屬性獲取下一個nmethod |
_osr_nmethods_head | 棧上替換的本地方法連結串列的頭元素 |
_init_state |
表示類的狀態,為列舉型別ClassState,定義瞭如下常量值:
|
_reference_type | 引用型別 |
_methods | 儲存方法的指標陣列 |
_default_methods | 儲存方法的指標陣列,從介面繼承的預設方法 |
_local_interfaces | 儲存介面的指標陣列,直接實現的介面Klass |
_transitive_interfaces | 儲存介面的指標陣列,包含_local_interfaces和通過繼承間接實現的介面 |
_default_vtable_indices | 預設方法在虛擬函式表中的索引 |
_fields |
類的欄位屬性,每個欄位的6個屬性access,、name index、sig index、initial value index、low_offset、high_offset組成一個元組, access表示訪問控制屬性,根據name index可以獲取屬性名,根據initial value index可以獲取初始值,根據low_offset與 high_offset可以獲取該屬性在記憶體中的偏移量。另外儲存完所有屬性之後還可能會儲存泛型簽名信息。 |
有了InstanceKlass與Klass中定義的這些屬性足夠用來儲存Java類元資訊。在後續的類解析中會看到對相關變數的屬性填充操作。除了儲存類元資訊外,此類還有另外一個重要的功能,即支援方法分派,主要是通過Java虛方法表和Java介面函式表來完成的,不過C++並不像Java一樣,儲存資訊時非要在類中定義出相關屬性,C++只是在分配記憶體時為要儲存的資訊分配好特定的記憶體,然後直接通過記憶體偏移來操作即可。
接下來幾個屬性是沒有對應的屬性名,只能通過指標和偏移量的方式訪問:
- Java vtable:Java虛擬函式表,大小等於_vtable_len;
- Java itables:Java介面函式表,大小等於 _itable_len;
- 非靜態oop-map blocks ,大小等於_nonstatic_oop_map_size;
- 介面的實現類,只有當前類表示一個介面時存在。如果介面沒有任何實現類則為NULL;如果只有一個實現類則為該實現類的Klass指標;如果有多個實現類,為當前類本身;
- host klass,只在匿名類中存在,為了支援JSR 292中的動態語言特性,會給匿名類生成一個host klass。
HotSpot在解析一個類時會呼叫InstanceKlass::allocate_instance_klass()方法分配記憶體,而分配多大的記憶體則是通過呼叫InstanceKlass::size()計算出來的,呼叫語句如下:
int size = InstanceKlass::size(vtable_len, itable_len, nonstatic_oop_map_size, isinterf, is_anonymous);
呼叫的size()方法的實現如下:
static int size(int vtable_length, int itable_length, int nonstatic_oop_map_size, bool is_interface, bool is_anonymous ){ return align_object_size(header_size() + // header_size()為55 align_object_offset(vtable_length) + align_object_offset(itable_length) + ( (is_interface || is_anonymous) ? align_object_offset(nonstatic_oop_map_size) : nonstatic_oop_map_size ) + ( is_interface ? (int)sizeof(Klass*)/HeapWordSize : 0 ) + ( is_anonymous ? (int)sizeof(Klass*)/HeapWordSize : 0) ); }
可以看到除了會為類中本身的屬性分配記憶體,也會為vtable與itable等分配記憶體。呼叫的header_size()方法就是計算此類的物件所佔用的記憶體大小,實現如下:
// Sizing (in words) static int header_size(){ return align_object_offset(sizeof(InstanceKlass)/HeapWordSize); // 以HeapWordSize為單位,64位一個字為8位元組,所以值為8 }
呼叫的align_object_offset()方法是進行記憶體對齊,這是一塊非常重要的C++知識點,後面會專門進行講解。
3、InstanceKlass類的子類
InstanceKlass共有3個直接子類,這3個子類用來表示一些特殊的類,下面簡單介紹一下這3個子類:
(1)InstanceRefKlass
java/lang/ref/Reference的子類需要使用InstanceRefKlass類來表示,因為這些類需要垃圾回收器特殊處理 ,在後續講解強引用、弱引用、虛引用以及幽靈引用時在詳細介紹。
(2)InstanceMirrorKlass類
用於表示特殊的java.lang.Class類,我們需要分清相關類的表示方法,如下圖所示。
java.lang.Class物件是通過對應的Oop物件來儲存類的靜態屬性,因此他們的例項大小不同,需要特殊的方式來計算他們的大小以及屬性遍歷。
Klass的屬性_java_mirror就指向儲存該類靜態欄位的Oop物件,可通過該屬性訪問類的靜態欄位。 Oop是HotSpot的物件表示模型,在後面會詳細介紹。
(3)InstanceClassLoaderKlass類
沒有新增新的欄位,增加了新的oop遍歷方法,主要用於類載入器依賴遍歷使用。
建立InstanceKlass例項會呼叫InstanceKlass::allocate_instance_klass()方法。在建立時,會涉及到C++new運算子的過載,通過過載new運算子來分配物件的記憶體空間,也就是呼叫InstanceKlass::size()方法得到的大小,然後再呼叫對應類的建構函式初始化相應的屬性。方法的實現如下:
InstanceKlass* InstanceKlass::allocate_instance_klass( ClassLoaderData* loader_data, int vtable_len, int itable_len, int static_field_size, int nonstatic_oop_map_size, ReferenceType rt, AccessFlags access_flags, Symbol* name, Klass* super_klass, bool is_anonymous, TRAPS) { bool isinterf = access_flags.is_interface(); int size = InstanceKlass::size(vtable_len, itable_len, nonstatic_oop_map_size, isinterf, is_anonymous); // Allocation InstanceKlass* ik; /////////////////////////////////////////////////////////////////////// if (rt == REF_NONE) { if (name == vmSymbols::java_lang_Class()) { ik = new (loader_data, size, THREAD) InstanceMirrorKlass( vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, access_flags, is_anonymous); } else if ( name == vmSymbols::java_lang_ClassLoader() || ( SystemDictionary::ClassLoader_klass_loaded() && super_klass != NULL && super_klass->is_subtype_of(SystemDictionary::ClassLoader_klass()) // ClassLoader_klass為java_lang_ClassLoader ) ){ ik = new (loader_data, size, THREAD) InstanceClassLoaderKlass( vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, access_flags, is_anonymous); } else { // normal class ik = new (loader_data, size, THREAD) InstanceKlass( vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, access_flags, is_anonymous); } } /////////////////////////////////////////////////////////////////////// else { // reference klass ik = new (loader_data, size, THREAD) InstanceRefKlass( vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, access_flags, is_anonymous); } /////////////////////////////////////////////////////////////////////// // 新增所有型別到我們內部類載入器列表中,包括在根載入器中的類 // Add all classes to our internal class loader list here, // including classes in the bootstrap (NULL) class loader. // loader_data的型別為ClassLoaderData*,通過ClassLoaderData中的_klasses保持通過InstanceKlass._next_link屬性保持的列表 loader_data->add_class(ik); return ik; }
方法的實現比較簡單,當rt等於REF_NONE時,也就是為非Reference型別時,會根據類名建立對應C++類的物件。Class類建立InstanceMirrorKlass、ClassLoader類或ClassLoader的子類建立InstanceClassLoaderKlass類、普通類通過InstanceKlass來表示。當rt不為REF_NONE時,會建立InstanceRefKlass物件。REF_NONE列舉常量的定義如下:
// ReferenceType is used to distinguish between java/lang/ref/Reference subclasses enum ReferenceType { REF_NONE, // Regular class REF_OTHER, // Subclass of java/lang/ref/Reference, but not subclass of one of the classes below REF_SOFT, // Subclass of java/lang/ref/SoftReference REF_WEAK, // Subclass of java/lang/ref/WeakReference REF_FINAL, // Subclass of java/lang/ref/FinalReference REF_PHANTOM // Subclass of java/lang/ref/PhantomReference };
可以看到,所有的Reference及子類都會用InstanceRefKlass來表示。當無法判斷到底是哪個子類時,會將Reference設定為REF_OTHER。
相關文章的連結如下:
1、在Ubuntu 16.04上編譯OpenJDK8的原始碼
2、除錯HotSpot原始碼
3、HotSpot專案結構
4、HotSpot的啟動過程
5、HotSpot二分模型 (1)
6、HotSpot的類模型(2)
&n