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源碼分析相關影片 //space.bilibili.com/27533329

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