HotSpot類模型之ArrayKlass
上一篇分析了 HotSpot類模型之InstanceKlass ,這次主要分析表示java陣列型別的C++類。
1、ArrayKlass類
ArrayKlass繼承自Klass,是所有陣列類的抽象基類,類及重要屬性的定義如下:
class ArrayKlass: public Klass { ... private: int _dimension; // This is n'th-dimensional array. Klass* volatile _higher_dimension; // Refers the (n+1)'th-dimensional array (if present). Klass* volatile _lower_dimension; // Refers the (n-1)'th-dimensional array (if present). int _vtable_len; // size of vtable for this klass oop _component_mirror; // component type, as a java/lang/Class ... }
在Klass的基礎上增加的屬性如下表所示。
欄位 | 作用 |
_dimension | int型別,表示陣列的維度,記為n |
_higher_dimension | Klass指標,表示對n+1維陣列Klass的引用 |
_lower_dimension | Klass指標,表示對n-1維陣列Klass的引用 |
_vtable_len | int型別, 虛擬函式表的長度 |
_component_mirror | oop, 陣列元素對應的java.lang.Class物件的Oop |
_vtable_len的值為5,因為陣列是引用型別,父類為Object類,而Object類中有5個虛方法可被用來繼承和重寫,如下:
void finalize() boolean equals(Object) String toString() int hashCode() Object clone()
_dimension、_higher_dimension與_lower_dimension對於一維及多維陣列的描述非常重要,屬性值的設定相對簡單,這裡不在介紹。
2、ArrayKlass類的子類
(1)TypeArrayKlass類
TypeArrayKlass是ArrayKlass的子類,用於表示陣列元素是基本型別的陣列
class TypeArrayKlass : public ArrayKlass { ... private: jint _max_length; // maximum number of elements allowed in an array ... }
_max_length表示該陣列允許的最大長度。
陣列類和普通類不同,陣列類沒有對應的Class檔案,所以陣列類是直接被虛擬機器建立的。HotSpot在初始化時就會建立好8個基本型別的一維陣列物件TypeArrayKlass。之前在講解HotSpot啟動時講到過,呼叫initializeJVM()方法初始化HotSpot,這個方法會最終呼叫到Universe::genesis()方法,在這個方法中初始化基本型別的一維陣列物件TypeArrayKlass。例如初始化boolean型別的一維陣列,呼叫語句如下:
_boolArrayKlassObj = TypeArrayKlass::create_klass(T_BOOLEAN, sizeof(jboolean), CHECK);
其中_boolArrayKlassObj是宣告在universe.cpp檔案中的全域性變數,如下:
Klass* Universe::_boolArrayKlassObj = NULL;
呼叫TypeArrayKlass::create_klass()方法建立TypeArrayKlass物件,具體就是呼叫TypeArrayKlass::create_klass()方法來完成,方法的實現如下:
TypeArrayKlass* TypeArrayKlass::allocate(ClassLoaderData* loader_data, BasicType type, Symbol* name, TRAPS) { int x = TypeArrayKlass::header_size(); int size = ArrayKlass::static_size(x); // 呼叫的建構函式在下面 return new (loader_data, size, THREAD) TypeArrayKlass(type, name); }
非常類似於InstanceKlass等物件的建立,首先獲取需要記憶體的大小size,然後通過過載new運算子完成物件記憶體分配後,呼叫TypeArrayKlass的建構函式初始化一些屬性。
TypeArrayKlass的header_size()及static_size()函式的實現如下:
static int header_size(){ int k = sizeof(TypeArrayKlass); return k/HeapWordSize; } int ArrayKlass::static_size(int header_size) { // size of an array klass object assert(header_size <= InstanceKlass::header_size(), "bad header size"); // If this assert fails, see comments in base_create_array_klass. header_size = InstanceKlass::header_size(); // 為什麼是InstanceKlass的大小??看ArrayKlass::start_of_vtable()函式有說明 int vtable_len = Universe::base_vtable_size(); // 值為5 int size = header_size + align_object_offset(vtable_len); // 對vtable_len進行對齊操作 return align_object_size(size); } static int header_size(){ return align_object_offset(sizeof(InstanceKlass)/HeapWordSize); }
注意header_size屬性的值應該是TypeArrayKlass這個類自身佔用的記憶體大小,但是現在卻取的是InstanceKlass這個類自身佔用記憶體的大小。這是因為InstanceKlass佔用記憶體大小比TypeArrayKlass大,有足夠記憶體存放相關資料,更重要的是為了統一從固定的偏移位置取出vtable_len屬性的值。這樣在實際操作過程中,無需關心是陣列還是類,都直接偏移固定位置後取vtable_len屬性值即可。
TypeArrayKlass的建構函式如下:
TypeArrayKlass::TypeArrayKlass(BasicType type, Symbol* name) : ArrayKlass(name) { int lh = array_layout_helper(type); set_layout_helper(lh); assert(oop_is_array(), "sanity"); assert(oop_is_typeArray(), "sanity"); set_max_length(arrayOopDesc::max_array_length(type)); // 設定陣列的最大長度 ... }
下面詳細介紹一下對_layout_helper屬性的設定。呼叫Klass::array_layout_helper()方法獲取_layout_helper屬性的值
jint Klass::array_layout_helper(BasicType etype) { assert(etype >= T_BOOLEAN && etype <= T_OBJECT, "valid etype"); // Note that T_ARRAY is not allowed here. int hsize = arrayOopDesc::base_offset_in_bytes(etype); // hsize表示陣列元素的物件頭部大小 int esize = type2aelembytes(etype); // 對應型別儲存所需要的位元組數 bool isobj = (etype == T_OBJECT); int tag = isobj ? _lh_array_tag_obj_value : _lh_array_tag_type_value; int esz = exact_log2(esize); int lh = array_layout_helper(tag, hsize, etype, esz); return lh; }
關於_layout_helper在之前已經介紹過,由於T_BOOLEAN為基本型別,所以tag的值取0xC0;hsize呼叫arrayOopDesc::base_offset_in_bytes()方法獲取,值為16,後面在講解arrayOopDesc時會介紹,陣列物件其實是由物件頭、物件欄位資料和對齊填充組成,而這裡獲取的就是物件頭的大小;esize表示對應型別儲存所需要的位元組數,對於T_BOOLEAN來說,只需要1個位元組即可,所以esz為0。最後呼叫array_layout_helper()方法按照約定組合成一個int型別的數字即可。array_layout_helper()方法的實現如下:
static jint array_layout_helper(jint tag, int hsize, BasicType etype, int log2_esize) { return (tag << _lh_array_tag_shift) // 左移30位 | (hsize << _lh_header_size_shift) // 左移16位 | ((int)etype << _lh_element_type_shift) // 左移1位 | (log2_esize << _lh_log2_element_size_shift); // 左移0位 }
另外還有對_component_mirror屬性的設定。對於一維基本型別的陣列來說,這個值是java.lang.Class物件。Class物件使用oop物件來表示,呼叫java_lang_Class::create_basic_type_mirror()方法獲取_component_mirror屬性的值,通過java_lang_Class::create_mirror()方法完成屬性的設定。例如獲取boolean型別的屬性值,呼叫語句如下:
void Universe::initialize_basic_type_mirrors(TRAPS) { ... _bool_mirror = java_lang_Class::create_basic_type_mirror("boolean",T_BOOLEAN, CHECK); ... }
方法create_basic_type_mirror()的實現如下:
oop java_lang_Class::create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS) { // This should be improved by adding a field at the Java level or by // introducing a new VM klass (see comment in ClassFileParser) oop java_class = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(NULL, CHECK_0); if (type != T_VOID) { Klass* aklass = Universe::typeArrayKlassObj(type); assert(aklass != NULL, "correct bootstrap"); set_array_klass(java_class, aklass); // 設定表示基本型別陣列的TypeArrayKlass的 } return java_class; }
通過InstanceMirrorKlass物件(表示java.lang.Class類)來建立oop(表示java.lang.Class物件),_component_mirror最終設定的就是這個oop。引用型別組成的一維或多維陣列的基本元素可以使用Klass物件來表示,如對於下面即將要介紹的Object[]來說,元素型別為Object,所以可以使用InstanceKlass來表示;基本型別組成的一維或多維陣列的基本元素沒有對應的Klass物件,所以只能使用Class物件來描述boolean、int等型別,這樣就會與表示Class物件的oop物件產生關係,相關屬性最終的值如下所示。
TypeArrayKlass._component_mirror=oop oop._array_klass_offset=TypeArrayKlass
oop表示java.lang.Class物件,用來描述Java類(包括陣列類),而TypeArrayKlass也用來描述Java類(包括陣列類),那麼2者之間必須會的聯絡。可以通過_component_mirror屬性(和_array_klass_offset屬性找到對方,屬性的設定過程在在java_lang_Class::create_mirror()函式中進行。
其它的屬性設定很簡單,這裡不在介紹。
(2)ObjArrayKlass類
ObjArrayKlass是ArrayKlass的子類,用於表示陣列元素是類或者陣列
class ObjArrayKlass : public ArrayKlass { ... private: Klass* _element_klass; // The klass of the elements of this array type Klass* _bottom_klass; // The one-dimensional type (InstanceKlass or TypeArrayKlass) ... }
該類新增了2個屬性,如下:
- _element_klass:陣列元素對應的Klass物件,如果是多維陣列,對應陣列元素的ObjArrayKlass物件
- _bottom_klass:一維陣列的型別,可以是InstanceKlass或者TypeArrayKlass。一維基本型別陣列為TypeArrayKlass,而二維基本型別陣列就會使用ObjArrayKlass來表示,所以其_bottom_klass會是TypeArrayKlass。
HotSpot在Universe::genesis()方法中建立Object陣列,如下:
InstanceKlass* ik = InstanceKlass::cast(SystemDictionary::Object_klass()); _objectArrayKlassObj = ik->array_klass(1, CHECK); // 呼叫表示Object類的InstanceKlass類的array_klass()方法
呼叫array_klass()方法時傳遞的引數1表示建立一維陣列。呼叫表示Object類的InstanceKlass物件的方法建立的,所以Object陣列的建立要依賴於InstanceKlass物件(表示Object類)進行建立。
傳遞的引數1表示建立Object的一維陣列型別,array_klass()函式及呼叫的相關函式的實現如下:
// array class with specific rank Klass* array_klass(int rank, TRAPS) { return array_klass_impl(false, rank, THREAD); } Klass* InstanceKlass::array_klass_impl(bool or_null, int n, TRAPS) { instanceKlassHandle this_oop(THREAD, this); return array_klass_impl(this_oop, or_null, n, THREAD); } Klass* InstanceKlass::array_klass_impl(instanceKlassHandle this_oop, bool or_null, int n, TRAPS) { if (this_oop->array_klasses() == NULL) { // 獲取_array_klasses屬性的值 if (or_null){ return NULL; } ResourceMark rm; JavaThread *jt = (JavaThread *)THREAD; { // Atomic creation of array_klasses MutexLocker mc(Compile_lock, THREAD); // for vtables MutexLocker ma(MultiArray_lock, THREAD); // Check if update has already taken place if (this_oop->array_klasses() == NULL) { ClassLoaderData* CLD = this_oop->class_loader_data(); Klass* k = ObjArrayKlass::allocate_objArray_klass(CLD, 1, this_oop, CHECK_NULL); this_oop->set_array_klasses(k); // 設定InstanceKlass::_array_klasses屬性的值 } } } // _this will always be set at this point ObjArrayKlass* oak = (ObjArrayKlass*)this_oop->array_klasses(); // 獲取InstanceKlass::_array_klasses屬性的值 if (or_null) { return oak->array_klass_or_null(n); } return oak->array_klass(n, CHECK_NULL); // 在創建出一維的引用型別陣列後,接著建立n維的引用型別陣列 }
首次建立ObjTypeKlass時,InstanceKlass::_array_klasses屬性的值為NULL,這樣就會呼叫objArrayKlass::allocate_objArray_klass()函式,創建出一維的引用型別陣列並儲存到了InstanceKlass::_array_klasses屬性中。有了一維的引用型別陣列後就可以接著呼叫array_klass()方法建立n維的引用型別陣列了。
(1)建立一維引用型別陣列ObjArrayKlass::allocate_objArray_klass()
Klass* ObjArrayKlass::allocate_objArray_klass( ClassLoaderData* loader_data, int n, KlassHandle element_klass, TRAPS ) { // Eagerly allocate the direct array supertype. KlassHandle super_klass = KlassHandle(); if (!Universe::is_bootstrapping() || SystemDictionary::Object_klass_loaded()) { KlassHandle element_super (THREAD, element_klass->super()); if (element_super.not_null()) { // element_super是Object,Object的父類是null // The element type has a direct super. E.g., String[] has direct super of Object[]. super_klass = KlassHandle(THREAD, element_super->array_klass_or_null()); bool supers_exist = super_klass.not_null(); // Also, see if the element has secondary supertypes. // We need an array type for each. Array<Klass*>* element_supers = element_klass->secondary_supers(); for( int i = element_supers->length()-1; i >= 0; i-- ) { Klass* elem_super = element_supers->at(i); if (elem_super->array_klass_or_null() == NULL) { supers_exist = false; break; } } if (!supers_exist) { // Oops. Not allocated yet. Back out, allocate it, and retry. KlassHandle ek; { MutexUnlocker mu(MultiArray_lock); MutexUnlocker mc(Compile_lock); // for vtables Klass* sk = element_super->array_klass(CHECK_0); super_klass = KlassHandle(THREAD, sk); for( int i = element_supers->length()-1; i >= 0; i-- ) { KlassHandle elem_super(THREAD, element_supers->at(i)); elem_super->array_klass(CHECK_0); } // Now retry from the beginning Klass* klass_oop = element_klass->array_klass(n, CHECK_0); // Create a handle because the enclosing brace, when locking // can cause a gc. Better to have this function return a Handle. ek = KlassHandle(THREAD, klass_oop); } // re-lock return ek(); } } else { // element_super不是Object // The element type is already Object. Object[] has direct super of Object. super_klass = KlassHandle(THREAD, SystemDictionary::Object_klass()); } } // Create type name for klass. Symbol* name = NULL; if ( !element_klass->oop_is_instance() || (name = InstanceKlass::cast(element_klass())->array_name()) == NULL ){ ResourceMark rm(THREAD); char *name_str = element_klass->name()->as_C_string(); int len = element_klass->name()->utf8_length(); char *new_str = NEW_RESOURCE_ARRAY(char, len + 4); int idx = 0; new_str[idx++] = '['; if (element_klass->oop_is_instance()) { // it could be an array or simple type new_str[idx++] = 'L'; } memcpy(&new_str[idx], name_str, len * sizeof(char)); idx += len; if (element_klass->oop_is_instance()) { new_str[idx++] = ';'; } new_str[idx++] = '\0'; name = SymbolTable::new_permanent_symbol(new_str, CHECK_0); if (element_klass->oop_is_instance()) { InstanceKlass* ik = InstanceKlass::cast(element_klass()); ik->set_array_name(name);// 設定InstanceKlass::_array_name的屬性 } } // Initialize instance variables ObjArrayKlass* oak = ObjArrayKlass::allocate(loader_data, n, element_klass, name, CHECK_0); // Add all classes to our internal class loader list here, // including classes in the bootstrap (NULL) class loader. // GC walks these as strong roots. loader_data->add_class(oak); // Call complete_create_array_klass after all instance variables has been initialized. ArrayKlass::complete_create_array_klass(oak, super_klass, CHECK_0); return oak; }
呼叫的 ObjArrayKlass::allocate()函式的實現如下:
ObjArrayKlass* ObjArrayKlass::allocate(ClassLoaderData* loader_data, int n, KlassHandle klass_handle, Symbol* name, TRAPS) { assert(ObjArrayKlass::header_size() <= InstanceKlass::header_size(),"array klasses must be same size as InstanceKlass"); int x = ObjArrayKlass::header_size(); int size = ArrayKlass::static_size(x); return new (loader_data, size, THREAD) ObjArrayKlass(n, klass_handle, name); } int ArrayKlass::static_size(int header_size) { // size of an array klass object assert(header_size <= InstanceKlass::header_size(), "bad header size"); // If this assert fails, see comments in base_create_array_klass. header_size = InstanceKlass::header_size(); // 為什麼是InstanceKlass的大小??看ArrayKlass::start_of_vtable()函式有說明 int vtable_len = Universe::base_vtable_size(); // 值為5 int size = header_size + align_object_offset(vtable_len); // 對vtable_len進行對齊操作 return align_object_size(size); }
ArrayKlass::complete_create_array_klass()函式的實現如下:
// Initialization of vtables and mirror object is done separatly from base_create_array_klass, // since a GC can happen. At this point all instance variables of the ArrayKlass must be setup. void ArrayKlass::complete_create_array_klass(ArrayKlass* k, KlassHandle super_klass, TRAPS) { ResourceMark rm(THREAD); Klass* curr_superklass = super_klass(); // super_klass是個引數,型別為KlassHandle k->initialize_supers(curr_superklass, CHECK); klassVtable* kv = k->vtable(); kv->initialize_vtable(false, CHECK); // 會初始化當前Klass的vtable(含有_length個vtableEntry) java_lang_Class::create_mirror(k, Handle(NULL), CHECK); }
呼叫initialize_vtalbe()完成虛擬函式表的初始化,呼叫java_lang_Class::create_mirror()函式完成當前ObjTypeArray物件對應的java.lang.Class物件的建立並設定了相關屬性。
(2)建立n維引用型別陣列ObjArrayKlass::array_klass()
Klass* ObjArrayKlass::array_klass_impl(bool or_null, int n, TRAPS) { assert(dimension() <= n, "check order of chain"); int dim = dimension(); if (dim == n) return this; if (higher_dimension() == NULL) { if (or_null) return NULL; ResourceMark rm; JavaThread *jt = (JavaThread *)THREAD; { MutexLocker mc(Compile_lock, THREAD); // for vtables // Ensure atomic creation of higher dimensions MutexLocker mu(MultiArray_lock, THREAD); // Check if another thread beat us if (higher_dimension() == NULL) { // Create multi-dim klass object and link them together Klass* k = ObjArrayKlass::allocate_objArray_klass(class_loader_data(), dim + 1, this, CHECK_NULL); ObjArrayKlass* ak = ObjArrayKlass::cast(k); ak->set_lower_dimension(this); OrderAccess::storestore(); set_higher_dimension(ak); assert(ak->oop_is_objArray(), "incorrect initialization of ObjArrayKlass"); } } } else { CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops()); } ObjArrayKlass *ak = ObjArrayKlass::cast(higher_dimension()); if (or_null) { return ak->array_klass_or_null(n); } return ak->array_klass(n, CHECK_NULL); } Klass* ObjArrayKlass::array_klass_impl(bool or_null, TRAPS) { return array_klass_impl(or_null, dimension() + 1, CHECK_NULL); }
最終表示Object類的InstanceKlass與表示一維陣列Object[]的ObjArrayKlass之間的相關屬性如下:
ObjArrayKlass._element_klass=InstanceKlass ObjArrayKlass._bottom_klass=InstanceKlass InstanceKlass._array_name="[Ljava/lang/Object;" InstanceKlass._array_klasses=ObjArrayKlass
ObjArrayKlass中其它的屬性設定也並不複雜,這裡不在介紹。
其它參考文章:
1、在Ubuntu 16.04上編譯OpenJDK8的原始碼(配視訊)
2、除錯HotSpot原始碼(配視訊)
3、HotSpot專案結構
4、HotSpot的啟動過程(配視訊進行原始碼分析)
5、HotSpot原始碼分析之C++物件的記憶體佈局
6、HotSpot原始碼分析之類模型
7、HotSpot類模型之InstanceKlass
搭建過程中如果有問題可直接評論留言或加作者微信mazhimazh。
作者持續維護的個人部落格 classloading.com。
B站上有HotSpot原始碼分析相關視訊 https://space.bilibili.com/27533329
關注公眾號,有HotSpot原始碼剖析系列文章!
&n