HotSpot的類模型(2)

在前一篇文章 HotSpot的二分模型 中已經講過,HotSpot採用了OOP-Klass模型描述Java的類和對象。Klass模型採用Klass類及相關子類來表示具體的Java類,能夠理解這些類爲Java類在C++ 中的對等體。通常 JVM 在加載 Class 文件時,會在方法區建立 Klass ,表示類的元數據,包括常量池、字段、方法等。html

相關類的繼承體系以下圖所示。java

  

Metadata是元數據類的基礎類型,除了Klass會直接繼承外,表示方法的Method與表示常量池的ConstantPool也會繼承,這裏只討論Klass繼承體系中涉及到的相關類。數組

整個Klass模型中涉及到的類主要提供了2個功能: app

(1)提供C++層面的Java類(包括Java類和Java數組)表示 less

(2)方法分派  ide

這一篇文章重點介紹一下Klass這個基礎類型。函數

一個Klass對象(注意是Klass對象表示Java類的元數據,因此不一樣的Java類就用不一樣的Klass對象)表明一個Java類的元數據(至關於java.lang.Class對象)。因此Klass中要有描述Java類中常量池、字段、方法的能力,也就是能保存這些信息,同時還能提供一些方法供HotSpot的開發者操做這些信息。oop

Klass類及重要屬性的定義以下:佈局

class Klass : public Metadata {
 ...
 protected:
  // note: put frequently-used fields together at start of klass structure
  // for better cache behavior (may not make much of a difference but sure won't hurt)
  enum { _primary_super_limit = 8 };
 
  // The "layout helper" is a combined descriptor of object layout.
  // For klasses which are neither instance nor array, the value is zero.
  //
  // For instances, layout helper is a positive number, the instance size.
  // This size is already passed through align_object_size and scaled to bytes.
  // The low order bit is set if instances of this class cannot be
  // allocated using the fastpath.
  //
  // For arrays, layout helper is a negative number, containing four
  // distinct bytes, as follows:
  //    MSB:[tag, hsz, ebt, log2(esz)]:LSB
  // where:
  //    tag is 0x80 if the elements are oops, 0xC0 if non-oops
  //    hsz is array header size in bytes (i.e., offset of first element)
  //    ebt is the BasicType of the elements
  //    esz is the element size in bytes
  // This packed word is arranged so as to be quickly unpacked by the
  // various fast paths that use the various subfields.
  //
  // The esz bits can be used directly by a SLL instruction, without masking.
  //
  // Note that the array-kind tag looks like 0x00 for instance klasses,
  // since their length in bytes is always less than 24Mb.
  //
  // Final note:  This comes first, immediately after C++ vtable,
  // because it is frequently queried.
  jint        _layout_helper;
 
  // The fields _super_check_offset, _secondary_super_cache, _secondary_supers
  // and _primary_supers all help make fast subtype checks.  See big discussion
  // in doc/server_compiler/checktype.txt
  //
  // Where to look to observe a supertype (it is &_secondary_super_cache for
  // secondary supers, else is &_primary_supers[depth()].
  juint       _super_check_offset;
 
  // Class name.  Instance classes: java/lang/String, etc.  Array classes: [I,
  // [Ljava/lang/String;, etc.  Set to zero for all other kinds of classes.
  Symbol*     _name;
 
  // Cache of last observed secondary supertype
  Klass*      _secondary_super_cache;
  // Array of all secondary supertypes
  Array<Klass*>* _secondary_supers;
  // Ordered list of all primary supertypes
  Klass*      _primary_supers[_primary_super_limit];
  // java/lang/Class instance mirroring this class
  oop         _java_mirror;
  // Superclass
  Klass*      _super;
  // First subclass (NULL if none); _subklass->next_sibling() is next one
  Klass*      _subklass;
  // Sibling link (or NULL); links all subklasses of a klass
  Klass*      _next_sibling;
 
  // All klasses loaded by a class loader are chained through these links
  Klass*      _next_link;
 
  // The VM's representation of the ClassLoader used to load this class.
  // Provide access the corresponding instance java.lang.ClassLoader.
  ClassLoaderData* _class_loader_data;
 
  AccessFlags _access_flags;    // Access flags. The class/interface distinction is stored here.
 
  markOop  _prototype_header;   // Used when biased locking is both enabled and disabled for this type
  
  ...
}

下表對各個屬性進行了簡單的介紹。 ui

字段名 做用
_layout_helper

對象佈局的綜合描述符。若是不是InstanceKlass或ArrayKlass,值爲0。若是是InstantceKlass或

ArrayKlass時,這個值是個組合數字。

對InstanceKlass而言,組合數字中包含有表示對象的以字節爲單位的內存佔用大小,也就是說

InstanceKlass表示Java類,由這個Java類建立的對象所須要的大小。

對ArrayKlass而言,該值是一個組合數字,包含4部分,具體怎麼組合和解析由子類實現:

  • tag:若是數組元素的類型爲對象,值爲0x80;不然值爲0xC0,表示數組元素的類型爲Java基本類型
  • sz::數組頭元素的字節數
  • ebt:數組元素的類型,枚舉值BasicType
  • esz:數組元素大小,以字節爲單位
_name 類名,如java/lang/String,[Ljava/lang/String
_primary_supers

Klass指針數組,大小固定爲8。_primary_supers表明了這個類的父類。例如IOException,是Exception的子類,

而Exception又是Throwable的子類。因此IOException的 _primary_supers是這樣的:

[Throwable, Exception, IOException]。若是繼承鏈過長,也就是當前類加上繼承的類多於8個(默認值,可經過命令更改)時,

會將多出來的類存儲到secondary_supers數組中。

 _super_check_offset 

快速查找supertype的一個偏移量,這個偏移量是相對於Klass對象起始地址的偏移量。若是當前類是IOException,

那麼這個屬性就指向_primary_supers數組中存儲IOException的位置。當存儲的類多於8個時,值與secondary_super_cache

相等。

_secondary_supers

Klass指針數組,通常存儲類實現的接口,偶爾還會存儲類

_secondary_super_cache

Klass指針,保存上一次查詢父類的結果

_java_mirror oopDesc指針,此類對應的java/lang/Class對象,能夠據此訪問類的靜態屬性
_super Klass指針,父類 
_subklass Klass指針,子類
_next_sibling Klass指針,該類的下一個子類,也就是經過_subklass->next_sibling()來獲取_subklass的兄弟子類
_next_link Klass指針,ClassLoader加載的下一個Klass
_class_loader_data ClassLoaderData指針,加載該類的ClassLoader
_access_flags 獲取類的修飾符,如private、final、static、abstract 、native等
_prototype_header 在鎖的實現過程當中很是重要,後續在介紹鎖時會介紹 

能夠看到,可以經過Klass類中的相關屬性保存Java類定義的一些信息,如_name保存Java類的名稱、_super保存Java類實現的類型等。Klass類是Klass模型中定義的類的基類,因此只保存了Java類的一些必要信息,其它如常量池、方法、字段等會經過具體子類的屬性來保存。 

類的屬性比較多,咱們在後面解釋類的過程當中能夠看到對相關屬性的賦值操做。下面來看一個對屬性賦值相對複雜一點的方法,如initialize_supers()方法,實現以下:

源代碼位置:hotspot/src/share/vm/oops/klass.cpp 

void Klass::initialize_supers(Klass* k, TRAPS) {
  // 當前類的父類k可能爲NULL,例如Object的父類爲NULL
  if (k == NULL) {
    set_super(NULL);           
    _primary_supers[0] = this;
  } else if (k != super() || k == SystemDictionary::Object_klass()) {

    set_super(k);    // 設置Klass的_super屬性
    Klass* sup = k;

    int sup_depth = sup->super_depth();
    juint my_depth  = MIN2(sup_depth + 1, (int)primary_super_limit()); // primary_super_limit()方法獲得的值通常默認爲8
    // 當父類的的繼承鏈長度大於等於primary_super_limit()時,當前的深度只能是primary_super_limit(),也就是8,由於_primary_supers中只存儲8個類
    if (!can_be_primary_super_slow()){
      my_depth = primary_super_limit(); // 8
    }
    for (juint i = 0; i < my_depth; i++) { // my_depth默認的值爲8
       _primary_supers[i] = sup->_primary_supers[i];
    }

    Klass* *super_check_cell;
    if (my_depth < primary_super_limit()) { // primary_super_limit()的默認爲8
       _primary_supers[my_depth] = this;
       super_check_cell = &_primary_supers[my_depth];
    } else {
       // Overflow of the primary_supers array forces me to be secondary.
       super_check_cell = &_secondary_super_cache;
    }
    // 經過_super_check_offset這個偏移量能夠快速定義到當前在_primary_supers中的位置
    juint  _super_check_offset = (address)super_check_cell - (address) this;
    set_super_check_offset( _super_check_offset ); // 設置Klass中的_super_check_offset屬性
  }

  // 第2部分代碼在下面
}

在設置當前類的父類時一般都會調用initialize_supers方法,同時也會設置_primary_supers、super_check_offset,若是繼承鏈過長,還有可能設置secondary_supers、secondary_super_cache等值。這此屬性中存儲繼承鏈中涉及到的類以方便快速的進行類關係之間的判斷,例如父子關係的判斷。  

方法的第2部分代碼實現以下:

if (secondary_supers() == NULL) {
    KlassHandle this_kh (THREAD, this);

    // Now compute the list of secondary supertypes.
    // Secondaries can occasionally be on the super chain,
    // if the inline "_primary_supers" array overflows.
    int extras = 0;
    Klass* p;
    for (p = super();
           // 當p不爲NULL而且p已經存儲在了_secondary_supers數組中時,條件爲true
           // 也就是當前類的父類多於8個,將多出來的存儲到了_secondary_supers數組中了
    	  !(p == NULL || p->can_be_primary_super());  
    	  p = p->super()) {
      ++extras;
    }

    // 計算secondaries須要的大小,由於secondaries數組中還須要存儲當前類的全部實現接口(包括直接和間接實現的接口)
    // Compute the "real" non-extra secondaries.
    GrowableArray<Klass*>* secondaries = compute_secondary_supers(extras);
    if (secondaries == NULL) { // extras爲0時直接返回,不須要額外的處理
      // secondary_supers set by compute_secondary_supers
      return;
    }

    GrowableArray<Klass*>* primaries = new GrowableArray<Klass*>(extras);
    for ( p = this_kh->super();
    	  !(p == NULL || p->can_be_primary_super());  
    	  p = p->super()
    ){
      primaries->push(p);
    }

    // Combine the two arrays into a metadata object to pack the array.
    // The primaries are added in the reverse order, then the secondaries.
    int new_length = primaries->length() + secondaries->length();
    Array<Klass*>* s2 = MetadataFactory::new_array<Klass*>(class_loader_data(), new_length, CHECK);
    int fill_p = primaries->length();
    for (int j = 0; j < fill_p; j++) {
       s2->at_put(j, primaries->pop());  // add primaries in reverse order.也就是父類永遠在數組前,子類永遠在數組後
    }
    for( int j = 0; j < secondaries->length(); j++ ) {
       s2->at_put(j+fill_p, secondaries->at(j));  // add secondaries on the end.
    }

    this_kh->set_secondary_supers(s2); // 設置_secondary_supers屬性
}

能夠看到,會將父親繼承鏈中多於8個的父類存儲到secondary_supers數組中,不過由於繼承鏈通常都不會多於8個,因此設置了默認值爲8。  

下面舉個例子,看一下是如何應用這幾個屬性的,例如is_subtype_of()方法,實現以下:

// subtype check: true if is_subclass_of, or if k is interface and receiver implements it
bool is_subtype_of(Klass* k) const { // 判斷當前類是否爲k的子類
    juint    off = k->super_check_offset();
    Klass*   sup = *(Klass**)( (address)this + off );

    const juint secondary_offset = in_bytes(secondary_super_cache_offset());

    if (sup == k) {
      return true;
    } else if (off != secondary_offset) { // ??沒弄明白這個邏輯,有大神能夠給指點下
      return false;
    } else {
      return search_secondary_supers(k);
    }
}

當經過_super_check_offset獲取到的類與k相同時,那麼k存在於當前類的繼承鏈上,確定有父子關係。

若是k存在於_primary_supers數組中,那麼經過_super_check_offset就可快速判斷,若是k存在於_secondary_supers中,那麼須要調用search_secondary_supers()來判斷。

調用的search_secondary_supers()方法的實現以下:

bool Klass::search_secondary_supers(Klass* k) const {
  // Put some extra logic here out-of-line, before the search proper.
  // This cuts down the size of the inline method.

  // This is necessary, since I am never in my own secondary_super list.
  if (this == k){
    return true;
  }
  // Scan the array-of-objects for a match
  int cnt = secondary_supers()->length();
  for (int i = 0; i < cnt; i++) {
    if (secondary_supers()->at(i) == k) {
      ((Klass*)this)->set_secondary_super_cache(k); // 設置_secondary_super_cache屬性,保存此次查詢的結果
      return true;
    }
  }
  return false;
}

能夠看到,屬性_secondary_super_cache保存了這一次父類查詢的結果。查詢的邏輯很簡單,遍歷_secondary_supers數組中的值並比較便可。  

另外還能調用Klass::append_to_sibling_list()函數設置_next_sibling與_subklass的值,方法的實現以下:

void Klass::append_to_sibling_list() {  

  // add ourselves to superklass' subklass list
  InstanceKlass* super = superklass(); // 獲取到_super屬性的值
  if (super == NULL)
	  return;   // special case: class Object

  Klass* prev_first_subklass = super->subklass_oop(); // 獲取_subklass屬性的值
  if (prev_first_subklass != NULL) {
    // set our sibling to be the superklass' previous first subklass
    set_next_sibling(prev_first_subklass); // 設置_next_sibling屬性的值
  }
  // make ourselves the superklass' first subklass
  super->set_subklass(this); // 設置_subklass屬性的值
}

方法的實現邏輯很簡單,這裏不過多介紹。  

還有對_layout_helper屬性的設置相對複雜,在講解InstanceKlass或ArrayKlass時介紹。  

 

相關文章的連接以下:

一、在Ubuntu 16.04上編譯OpenJDK8的源代碼 

二、調試HotSpot源代碼

三、HotSpot項目結構 

四、HotSpot的啓動過程 

五、HotSpot二分模型(1)

六、HotSpot的類模型(2)  

七、HotSpot的類模型(3) 

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

相關文章
相關標籤/搜索